/***************************************************************************
 *   copyright           : (C) 2002 by Hendrik Sattler                     *
 *   mail                : post@hendrik-sattler.de                         *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "memtypes.h"
#include "scmxx.h"
#include "helper.h"
#include "options.h"
#include "atcommand.h"
#include "ttyaccess.h"
#include "pbookfile.h"
#include "gtincl.h"

#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
//needed by at least Solaris8
#include <strings.h>

void sms_manage (int action, char** files,
		 char* outfile, char* pipe,
		 struct parameters* myparams,
		 struct smsopts* mysmsopts)
{
  char* storetype;
  char** memlist;
  int i = 0;
  struct slot_range range;
  int current_fill;
  struct sms_slot_data** slist;
  struct sms_pdu_raw** pdu;
  int mark_read = 1; //getting SMS with changing status from "unread" to "read"
    
  memlist = NULL;
  slist = NULL;

  if (str_len(myparams->mem) != 0 &&
      strcmp(myparams->mem,"?") == 0) {
    sms_print_memlist(NULL,1);
    return;
  }

  if (action == 0) {
    errexit ("%s\n",_("You must specify a valid action."));
  }

  if (myparams->slot == SCMXX_SLOT_UNDEFINED) {
    switch (action) {
    case SCMXX_ACTION_GET:
      myparams->slot = SCMXX_SLOT_SMS_UNREAD;
      break;
    case SCMXX_ACTION_SEND:
      break;
    default:
      errexit("%s\n",_("If you want to remove short messages or mix actions, you must define a slot or slot type."));
      break;
    }
  }

  if (str_len(myparams->mem)) {
    storetype = myparams->mem;
  } else {
    memlist = sms_get_types();
    if (memlist[i] == NULL) {
      mem_realloc(memlist,0);
      return;
    } else {
      do {
	if (!strcasecmp(memlist[i],"MT")) {
	  break;
	}
      } while (memlist[++i] != NULL);
      if (memlist[i] == NULL) { // MT not present
	i=0;
	do {
	  if (!strcasecmp(memlist[i],"SM")) {
	    break;
	  }
	} while (memlist[++i] != NULL);
	if (memlist[i] == NULL) { // SM not present (should never happen)
	  i=0; //choosing first one in list
	}
      }
    }
    storetype=memlist[i];
  }
  if (!sms_select_mem(storetype,&range,&current_fill)) {
    errexit(_("SMS storage type \"%s\" could not be selected.\n"),storetype);
  }
  if (memlist != NULL) mem_realloc(*memlist,0);
  mem_realloc(memlist,0);

  if (str_len(outfile) == 0 && str_len(pipe) == 0) {
    mark_read = 0;
  }
    
  if (action&SCMXX_ACTION_GET) {
    if (mysmsopts->direct) {
      //getting messages as unexpected notice from the phone
      sms_get_direct(outfile,pipe);
    } else {
      do {
	if (i > current_fill) {
	  current_fill = i;
	}
	slist = mem_alloc((current_fill+1)*sizeof(*slist),0);
	i = sms_get(slist,current_fill+1,myparams->slot,mark_read);
      } while (i > current_fill);
      sms_print_all(outfile,pipe,slist,mysmsopts->sort_mode);
    }
  }
  if (action&SCMXX_ACTION_SEND) {
    if (myparams->slot != SCMXX_SLOT_UNDEFINED &&
	(myparams->slot < SCMXX_SLOT_SMS_MIN ||
	 (myparams->slot >= SCMXX_SLOT_SMS_MIN && 
	  str_len(myparams->number) == 0))) {
      if (slist == NULL) {
	do {
	  if (i > current_fill) {
	    current_fill = i;
	  }
	  slist = mem_alloc((current_fill+1)*sizeof(*slist),0);
	  i = sms_get(slist,current_fill+1,myparams->slot,mark_read);
	} while (i > current_fill);
      }
      if (slist != NULL) {
	/* These mails come from SMS storage and thus are sent, not stored.
	 * This was also the behaviour in previous versions.
	 */
	sms_send_pdu_all(slist,myparams->number,1);
      }
    } else {
      if (myparams->slot >= SCMXX_SLOT_SMS_MIN) {
	sms_send_slot(myparams->slot,myparams->number);
      } else {
	if (myparams->text == NULL &&
	    (files == NULL || str_len(files[0]) == 0)) {
	  print_verbose(0,"%s\n",_("Hmm, nothing to send?"));
	}
	if (myparams->text != NULL) {
	  pdu = sms_pdu_create(NULL,myparams->text,myparams->number,mysmsopts);
	  sms_send_pdus(pdu,mysmsopts->direct);
	  //so we do not submit the same sms multiple times
	  myparams->text = NULL;
	}
	if (files != NULL) {
	  i=0;
	  while (files[i] != NULL) {
	    pdu = sms_pdu_create(files[i++],NULL,myparams->number,mysmsopts);
	    sms_send_pdus(pdu,mysmsopts->direct);	
	  }
	}
      }
    }
  }
  if (action&SCMXX_ACTION_REMOVE) {
    if (slist == NULL &&
	myparams->slot < SCMXX_SLOT_SMS_MIN) {
      do {
	if (i > current_fill) {
	  current_fill = i;
	}
	slist = mem_alloc((current_fill+1)*sizeof(*slist),0);
	i = sms_get(slist,current_fill+1,myparams->slot,mark_read);
      } while (i > current_fill);
    }
    if (slist != NULL) {
      sms_delete_all(slist);
    } else {
      sms_delete_slot(myparams->slot);
    }
  }
}

void sms_delete_slot (int smsslot) {
  char* ack;

  if (smsslot >= SCMXX_SLOT_SMS_MIN) {
    at_gen_sms_delete(smsslot);
    ack = at_read_line();
    if (!strcmp(ack,"OK")) {
      print_verbose(0,_("SMS slot %d was deleted.\n"),smsslot);
    } else if(!strcmp(ack,"+CMS ERROR: INVALID MEMORY INDEX")) {
      errexit("%s\n",_("This slot is not available."));
    } else if(strstr(ack,"ERROR")!=NULL) {
      errexit("%s\n",ack);
    }
    mem_realloc(ack,0);
  } else {
    errexit("%s\n",_("You must specify a valid slot number."));
  }
}

void sms_delete_all (struct sms_slot_data** slist) {
  size_t i = 0;

  if (slist == NULL) {
    return;
  }
  while (slist[i] != NULL) {
    if (slist[i]->slot != SMS_SLOT_INVALID) {
      sms_delete_slot(slist[i]->slot);
    }
    ++i;
  }
}

char* sms_pdu_number_lookup (char* number) {
  char* filename;
  struct pbook_entry** cache = NULL;
  struct pbook_entry** found = NULL;
  unsigned int i;
  unsigned int k = 0;
  ucs4char_t* entry_name;
  char* entry_number = NULL;

  //if number is not a number but text, look it up
  if (is_telephone_number(number) == 0) {
    filename = configfile_get_path (PACKAGE_NAME,PBOOK_FILE_CACHENAME);
    if (filename == NULL ||
	file_accessible_ro(filename) == 0) {
      errexit("%s\n",_("Argument to --number is not a number but phonebook cache is not present."));
    } else {
      cache = pbook_file_read(filename);
      entry_name = convert_from_system(number);
      while (cache[k] != NULL) ++k; 
      found = mem_alloc((k+1)*sizeof(*found),0);
      k = 0;
      found[k] = NULL;
      for (i = 0; cache[i] != NULL; ++i) {
	if (ucs4str(cache[i]->text,entry_name) != NULL) {
	  found[k++] = cache[i];
	  found[k] = NULL;
	}
      }
      mem_realloc(entry_name,0);
      if (found[0] == NULL) {
	errexit(_("no cache entry for \"%s\" found.\n"),number);
      } else if (found[1] != NULL) {
	print_error(_("no unique hit in cache for search term \"%s\":\n"),
		    (number == NULL) ? "" : number);
	for (k = 0; found[k] != NULL; ++k) {
	  entry_number = convert_to_system(found[k]->text,REPMODE_QUESTIONMARK);
	  fprintf(stderr,"%s: %s\n",entry_number,found[k]->number);
	  entry_number = mem_realloc(entry_number,0);
	}
	exit(EXIT_FAILURE);
      } else {
	return str_dup(found[0]->number);
      }
      for (k = 0; cache[k] != NULL; ++k) {
	mem_realloc(cache[k]->number,0);
	mem_realloc(cache[k]->text,0);
	mem_realloc(cache[k],0);
      }
      mem_realloc(cache,0);
      mem_realloc(found,0);
    }
  } else {
    return str_dup(number);
  }
  return NULL;
}

void sms_send_slot (int smsslot, char* smsnumber) {
  char* ausgabe;
  char* entry_number = NULL;

  entry_number = sms_pdu_number_lookup(smsnumber);
  if (str_len(entry_number) == 0) errexit("%s\n",_("zero-length number"));
  at_gen_sms_slot_send(smsslot,entry_number);
  mem_realloc(entry_number,0);
  ausgabe = at_read_line();
  if (!strcmp(ausgabe,"+CMS ERROR: INVALID MEMORY INDEX")) {
    errexit("%s\n",_("This slot is not available."));
  } else if(strstr(ausgabe,"ERROR")!=NULL) {
    errexit("%s\n",ausgabe);
  }
  print_verbose(0,"%s %s: %s\n",_("The message was sent."),_("Message reference"),ausgabe+7);
  mem_realloc(ausgabe,0);
  ausgabe = at_read_line();
  print_verbose(0,"%s: %s\n",_("The phone returned"),ausgabe);
  mem_realloc(ausgabe,0);
}

void sms_send_pdus (struct sms_pdu_raw** pdu, int direct) {
  unsigned int i = 0;

  for (; pdu[i] != NULL; ++i)
    sms_send_pdu(pdu[i],direct);
}

void sms_send_pdu (struct sms_pdu_raw* tpdu, int direct) {
  char* command;
  char* ausgabe;
  char* ack;
  char* pdustr = sms_pdu_raw_to_hexstring(tpdu);

  if (direct) {
    at_gen_sms_send(sms_tpdu_len(SMS_OUTGOING,tpdu));
    command = AT_GEN_SMS_SEND;
  } else {
    at_gen_sms_store(sms_tpdu_len(SMS_OUTGOING,tpdu));
    command = AT_GEN_SMS_STORE;
  }

  print_verbose(0,"%s\n",_("Waiting for data request..."));
  ack = at_read_line();
  if (!strncmp(ack,"> ",2)) {
    mem_realloc(ack,0);
    print_verbose(0,"%s\n",_("Sending data..."));
    at_data_send(pdustr,strlen(pdustr));
    ausgabe = at_read_line();
    if (strstr(ausgabe,"ERROR")!=NULL) {
      errexit(_("An error occured on sending the SMS: %s\n"),ausgabe);
    }
    if (direct) {
      print_verbose(0,"%s %s: %s\n",_("The message was sent."),_("Message reference"),ausgabe+7);
    } else {
      print_verbose(0,_("The message was saved to SMS memory slot %s\n"),ausgabe+7);
    }
    mem_realloc(ausgabe,0);
    ack = at_read_line();
    print_verbose(0,"%s: %s\n",_("The phone returned"),ack);
  } else {
    if (!strncmp(ack,"ERROR",5)) {
      errexit("%s\n",_("An unknown error occured."));
    } else if (!strcasecmp(ack,"+CMS ERROR: MEMORY FULL")) {
      errexit("%s\n",_("There is no slot free to store the sms."));
    } else {
      errexit("%s\n",ack+12);
    }
  }
  mem_realloc(ack,0);
  mem_realloc(pdustr,0);
}

void sms_send_pdu_all (struct sms_slot_data** slist, char* number, int direct) {
  int i = 0;
  
  if (slist == NULL || *slist == NULL) {
    return;
  }
  while (slist[i] != NULL) {
    if (slist[i]->slot != SMS_SLOT_INVALID) {
      print_verbose(0,_("Trying to send SMS from slot %d...\n"),slist[i]->slot);
    }
    if (slist[i]->dir != SMS_OUTGOING) {
      print_verbose(0,"%s\n",_("Sending is only possible with SMS of outgoing type."));
    } else {
      if (str_len(number) && slist[i]->slot != SMS_SLOT_INVALID) {
	sms_send_slot(slist[i]->slot, number);
      } else {
	sms_send_pdu(&slist[i]->tpdu,direct);
      }
    }
    ++i;

  }
}

struct sms_pdu_raw** sms_pdu_create (char* file, char* text, char* number, 
				     struct smsopts* mysmsopts)
{
  struct sms_pdu_raw** pdu;
  char* smsinput;
  unsigned int smsinputsize;
  unsigned int i;
  char* entry_number = NULL;
  unsigned long flags = 0;
  
  if (str_len(text) == 0) {// --sms-text was not used, look for other sources
    smsinputsize = file_read_binary(file,&smsinput);
    if (smsinputsize == 0) {
      errexit("%s\n",_("No SMS text found (or zero length)."));
    }
  } else {
    smsinput = str_dup(text);
  }

  entry_number = sms_pdu_number_lookup(number);
  print_verbose(0,"%s\n",_("Creating PDU..."));
  if (mysmsopts->flash) flags |= SMS_CREATE_OPT_FLASH;
  if (mysmsopts->srr) flags |= SMS_CREATE_OPT_REPORT;
  if (mysmsopts->unicode) flags |= SMS_CREATE_OPT_UNICODE;
  pdu = sms_pdu_create_submit(smsinput,entry_number,flags);
  if (pdu == NULL)
    errexit("%s\n",_("short message encoding failed"));

  mem_realloc(entry_number,0);
  mem_realloc(smsinput,0);
  for (i=0; pdu[i] != NULL; ++i) {
    smsinput = sms_pdu_raw_to_hexstring(pdu[i]);
    print_verbose(1,"PDU[%d]: %s\n",i,smsinput);
    mem_realloc(smsinput,0);
  }

  return pdu;
}

/* slist must be of size (ssize*sizeof(struct sms_slot_data*))
 * slist ends with NULL
 * If all entries (and NULL) fit into slist, 0 is returned
 * else the suggested number of entries is returned.
 *
 * The pointers in slist are malloc'ed and need to be free'd.
 */
int sms_get (struct sms_slot_data** slist, size_t ssize, int slot, int mark_read) {
  char* command;
  char* ausgabe;
  char* temp;
  size_t retval = 0;
  int type = 0;
  enum return_code status;
  int listval = -1;
  char* mark1 = NULL;
  char* mark2 = NULL;

  if (slot >= SCMXX_SLOT_SMS_MIN) {
    if (mark_read) {
      command = at_gen_sms_slot_read(slot);
    } else {
      command = at_sie_sms_slot_read(slot);
      if (command == NULL)
	errexit("%s\n", _("You must define an output target."));
    }
  } else {
    switch (slot) {
    case SCMXX_SLOT_ALL: listval = 4; break;
    case SCMXX_SLOT_SMS_SENT: listval = 3; break;
    case SCMXX_SLOT_SMS_UNSENT: listval = 2; break;
    case SCMXX_SLOT_SMS_READ: listval = 1; break;
    case SCMXX_SLOT_SMS_UNREAD: listval = 0; break;
    default: return 0;
    }
    if (mark_read) {
      command = at_gen_sms_list_read(listval);      
    } else {
      command = at_sie_sms_list_read(listval);      
      if (command == NULL)
	errexit("%s\n", _("You must define an output target."));
    }
  }
  ausgabe = at_read_line();
  /* example:
   * +CMGR: 1,,159
   * +CMGL: 2,2,,14
   */
  switch (at_line_type(ausgabe,command,&temp)) {
  case AT_RET_ANSWER:
  case AT_RET_OTHER:
    break;
  case AT_RET_ERROR:
    errexit("%s\n",_("unknown cause"));
  case AT_RET_ERROR_CME:
  case AT_RET_ERROR_CMS:
    if (strcasecmp(temp,"INVALID MEMORY INDEX") == 0) {
      errexit("%s\n",_("This slot is not available."));
    } else {
      errexit("%s\n",temp);
    }
    break;
  case AT_RET_OK:
    switch (slot) {
    default:
    case SCMXX_SLOT_ALL: temp = NULL ; break;
    case SCMXX_SLOT_SMS_SENT: temp = _("sent"); break;
    case SCMXX_SLOT_SMS_UNSENT: temp = _("unsent"); break;
    case SCMXX_SLOT_SMS_READ: temp = _("read"); break;
    case SCMXX_SLOT_SMS_UNREAD: temp = _("unread"); break;
    }
    if (temp != NULL) {
      errexit(_("There are no %s short messages on the phone.\n"),temp);
    } else {
      errexit("%s",_("There are no short messages on the phone.\n"));
    }
    temp = NULL;
    break;
  }

  if (slot < SCMXX_SLOT_SMS_MIN) {
    print_verbose(0,"%s\n",_("Looking for SMS of specified type..."));
  }
  do {
    slist[retval]=mem_alloc(sizeof(**slist),0);
    sms_slot_data_init(slist[retval]);
    if (slot >= SCMXX_SLOT_SMS_MIN) {
      slist[retval]->slot = (unsigned int)slot;
    } else {
      slist[retval]->slot = atoi(temp);
      temp = strchr(temp,',');
      if (temp == NULL) errexit("%s\n",_("Output parsing error."));
      ++temp;
    }
    type = atoi(temp);
    if (type < 2) {
      slist[retval]->dir = SMS_INCOMING;
    } else {
      slist[retval]->dir = SMS_OUTGOING;
    }
    mem_realloc(ausgabe,0);
    ausgabe = at_read_line();
    if (!strcmp(ausgabe,"OK")) {
      errexit(_("Slot %u is empty.\n"), slist[retval]->slot);
    } else {
      switch (type) {
      case 0: mark1 = _("incoming"); mark2 = _("unread"); break;
      case 1: mark1 = _("incoming"); mark2 = _("read"); break;
      case 2: mark1 = _("outgoing"); mark2 = _("unsent"); break;
      case 3: mark1 = _("outgoing"); mark2 = _("sent"); break;
      default: break;
      }
      if (mark1 != NULL && mark2 != NULL) {
	print_verbose(0,_("Found %s, %s SMS in slot %u.\n"),
		 mark1, mark2, slist[retval]->slot);
      } else {
	print_verbose(0,_("Found SMS of unknown type in slot %u.\n"),
		      slist[retval]->slot);
      }
      if (strlen(ausgabe) >= sizeof(slist[retval]->tpdu)*2) {
	errexit("%s\n",_("Returned PDU exceeds buffer size."));
      } else {
	sms_pdu_raw_init(&slist[retval]->tpdu);
	sms_pdu_raw_from_hexstring(&slist[retval]->tpdu,ausgabe);
      }
      mem_realloc(ausgabe,0);
      ausgabe = at_read_line();
    }
    status = at_line_type(ausgabe,command,&temp);
  } while (retval++ <= ssize &&
	   (status == AT_RET_ANSWER || status == AT_RET_OTHER));
  mem_realloc(ausgabe,0);
  slist[retval] = NULL;
  return retval;
}

void sms_print (FILE* filefd, char* pipe, struct sms* data) {
  FILE* pipefd = NULL;

  if (filefd != NULL) { //send output to a file
    sms_pdu_print(filefd,data,SMS_PRINT_ALL_HEADERS);
  }

  if (str_len(pipe)) { //also send output (splitted) to a given pipe
    pipefd = popen(pipe,"w");
    if (pipefd != NULL) {
      sms_pdu_print(pipefd,data,SMS_PRINT_ALL_HEADERS);
      if (pclose(pipefd) == -1) {
	fprintf(stderr,"\n");
	print_warning(_("closing pipe \"%s\" failed.\n"),pipe);
      }
    }
  }
}

int strn_reverse_compare (const char* s1, const char* s2, size_t n)
{
  size_t s1len = str_len(s1);
  size_t s2len = str_len(s2);

  if (s1 == NULL && s2 == NULL) return 1;
  if (s1 == NULL || s2 == NULL) return 0;
  if (s1len < n) {
    if(s2len >= n) {
      return 0;
    } else { //both are smaller than n
      if (s1len != s2len) return 0;
      else n = s1len;
    }
  } else {
    if (s2len < n) return 0;
    /* else both are longer than n */
  }
  
  if (strcmp(s1+s1len-n,s2+s2len-n) != 0) return 0;
  else return 1;
}

void sms_print_all (char* file, char* pipe,
		    struct sms_slot_data** slist,
		    char* sort_mode)
{
  struct sms** data = NULL;
  FILE* filefd = NULL;
  unsigned int i = 0;
  unsigned int k = 0;
  struct sms_tpdu* decoded;
  char* filename;
  struct pbook_entry** cache = NULL;
  struct pbook_entry** found = NULL;
  unsigned int fcount = 0;
  unsigned int hitlen;
  struct gsm_number* addr;

  if (slist == NULL ||
      (str_len(file) == 0 && str_len(pipe) == 0)) {
    return;
  }

  if (str_len(file) != 0) {
    if (!strcmp(file,"-")) {
      filefd=stdout;
    } else {
      filefd=fdopen(file_open_output(file),"w+");
    }
  }

  //decoding
  while (slist[i] != NULL) ++i;
  if (i == 0) return;
  data = mem_alloc((i+1)*sizeof(*data),0);
  for (i = 0; slist[i] != NULL; ++i) {
    data[i] = NULL;
    decoded = sms_pdu_decode(slist[i]);
    if (decoded == NULL || decoded->pdu == NULL) {
      ++k;
    } else {
      data[i-k] = mem_alloc(sizeof(**data),0);
      sms_init(data[i-k],slist[i],decoded);
    }
  }
  data[i] = NULL;  

  //matching status report
  sms_statusreport_match(data);

  //merging
  if (sms_multipart_merge(data) < 0) {
    print_error("%s\n",_("Concatenated messages cannot be reassembled."));
  }

  //sorting
  sms_pdu_sort(data,sort_mode);

  //looking up number in cache
  filename = configfile_get_path (PACKAGE_NAME,PBOOK_FILE_CACHENAME);
  if (filename != NULL &&
      file_accessible_ro(filename) == 1) {
    cache = pbook_file_read(filename);
    while (cache[k] != NULL) ++k; 
    found = mem_alloc((k+1)*sizeof(*found),0);
    //assigning numbers
    for (i = 0; data[i] != NULL; ++i) {
      addr = &(data[i]->decoded->pdu->address);
      if (addr->text == NULL) {
	hitlen = 5;
	do {
	  //(re-)init loop
	  fcount = 0;
	  found[0] = NULL;
	  //register matching entries
	  for (k = 0; cache[k] != NULL; ++k) {
	    if (strn_reverse_compare(cache[k]->number,
				     addr->digits,hitlen) == 1) {
	      found[fcount++] = cache[k];
	      found[fcount] = NULL;
	    }
	  }
	  //evaluate if only one match was found
	  if (found[0] == NULL) {
	    print_verbose(1,_("\nNo cache hit for number \"%s\" with match size %d\n"),
		     addr->digits,hitlen);
	  } else if (found[1] == NULL) {
	    print_verbose(1,_("\nCache hit for number \"%s\" with match size %d\n"),
		     found[0]->number,hitlen);
	    addr->text = ucs4dup(found[0]->text);
	  } else {
	    print_verbose(1,_("\nNo unique hit in cache for number \"%s\" with match size %d\n"),
		     addr->digits,hitlen);
	    //try to be more specific on the match
	    ++hitlen;
	    //additinal loop break condition
	    if (hitlen > strlen(addr->digits)) break;
	  }
	} while (found[0] != NULL && found[1] != NULL);
      }
    }
    //cleaning up cache
    for (k = 0; cache[k] != NULL; ++k) {
      mem_realloc(cache[k]->number,0);
      mem_realloc(cache[k]->text,0);
      mem_realloc(cache[k],0);
    }
    mem_realloc(cache,0);
    mem_realloc(found,0);
  }

  //printing
  for (i = 0; data[i] != NULL; ++i) {
    sms_print(filefd,pipe,data[i]);
  }

  if (str_len(file) && strcmp(file,"-")) {
    fclose(filefd);
  }
}

void sms_get_direct (char* file, char* pipe) {
  char* ausgabe;
  char* temp;
  FILE* filefd = NULL;
  struct sms data;
  struct sms_slot_data* encoded;
  struct sms_tpdu* decoded;
  enum return_code status;
	
  ausgabe = NULL;
  temp = NULL;

  if (str_len(file) == 0 && str_len(pipe) == 0) {
    errexit("%s\n",_("No output method was specified."));
  }

  at_gen_sms_phase(1);
  ausgabe = at_read_line();
  status = at_line_type(ausgabe,AT_GEN_SMS_PHASE,NULL);
  mem_realloc(ausgabe,0);
  if (status == AT_RET_ANSWER || status == AT_RET_OTHER) {
    mem_realloc(at_read_line(),0);
  } else {
    errexit("%s\n",_("Could not set Phase 2+ compatible mode."));
  }

  
  at_gen_msg_direct (1,2,2,1);
  ausgabe = at_read_line();
  status = at_line_type(ausgabe,AT_GEN_MSG_DIRECT,NULL);
  mem_realloc(ausgabe,0);
  if (status != AT_RET_OK) {
    errexit("%s\n",_("Could not set direct mode."));
  }

  if (str_len(file) != 0) {
    if (!strcmp(file,"-")) {
      filefd = stdout;
    } else {
      filefd = fdopen(file_open_input(file),"w+");
    }
  }

  ausgabe = NULL;
  while (1) {
    do {
      mem_realloc(ausgabe,0);
      ausgabe = at_read_line();
    } while (str_len(ausgabe) == 0);

    if (at_line_type(ausgabe,"+CMT",NULL) == AT_RET_ANSWER /* SMS-DELIVER */
	|| at_line_type(ausgabe,"+CDS",NULL) == AT_RET_ANSWER /* SMS-STATUS-REPORT */) {
      mem_realloc(ausgabe,0);
      ausgabe = at_read_line();
      encoded = mem_alloc(sizeof(*encoded),0);
      sms_slot_data_init(encoded);
      encoded->slot = SMS_SLOT_INVALID;
      encoded->dir = SMS_INCOMING;
      sms_pdu_raw_from_hexstring(&encoded->tpdu,ausgabe);

      //decode and print the short message
      //does not handle concatenated short messages
      decoded = sms_pdu_decode(encoded);
      if (decoded == NULL || decoded->pdu == NULL) {
	errexit("%s\n",_("Failed to decode PDU."));
      }
      sms_init(&data,encoded,decoded);
      sms_print(filefd,pipe,&data);
      sms_delete(&data);
      encoded = NULL;
      decoded = NULL;

      //if this is not sent, SMSC will resent the SMS so we would receive it multiple times
      mem_realloc(ausgabe,0);
      at_command_send(AT_GEN_MSG_ACK,"0");
      ausgabe = at_read_line();
      status = at_line_type(ausgabe,NULL,NULL);
      if (status != AT_RET_OK) {
	errexit("%s\n",_("SMS ACK should have been ok but was not."));
      }
    } else if (at_line_type(ausgabe,"+CMTI",NULL) == AT_RET_ANSWER
	       || at_line_type(ausgabe,"+CDSI",NULL) == AT_RET_ANSWER) {
      //not the message itself but an indication to it (very rare case)
      print_notice("%s\n",_("A new message was stored in the phone."));
    } else if (at_line_type(ausgabe,"+CBM",NULL) == AT_RET_ANSWER) {
      mem_realloc(ausgabe,0);
      ausgabe = at_read_line();
      print_notice("\n%s%s\n\n",
		   "A cell broadcast message was received, but decoding is not implemented, yet.\n"
		   "I would like to implement cell broadcast message decoding but have no examples.\n"
		   "Please mail this to post@hendrik-sattler.de, including the following PDU:\n",
		   ausgabe);
    }
  }
}
