/** * @name action_send_messages: */ int action_send_messages(gammu_state_t **sp, int argc, char *argv[]) { int rv = 0; char **argp = &argv[1]; if (argc <= 2) { print_usage_error(U_ERR_ARGS_MISSING); return 1; } if (argc % 2 != 1) { print_usage_error(U_ERR_ARGS_ODD); return 2; } /* Lazy initialization of libgammu */ gammu_state_t *s = gammu_create_if_necessary(sp); if (!s) { print_operation_error(OP_ERR_INIT); rv = 3; goto cleanup; } /* Allocate */ smsc_t *smsc = allocate(sizeof(*smsc)); multimessage_t *sms = allocate(sizeof(*sms)); multimessage_info_t *info = allocate(sizeof(*info)); /* Find SMSC number */ smsc->Location = 1; if ((s->err = GSM_GetSMSC(s->sm, smsc)) != ERR_NONE) { print_operation_error(OP_ERR_SMSC); rv = 4; goto cleanup_sms; } transmit_status_t status; initialize_transmit_status(&status); GSM_SetSendSMSStatusCallback( s->sm, _message_transmit_callback, &status ); boolean_t is_start = TRUE; unsigned int message_index = 0; printf("["); /* For each message... */ while (*argp != NULL) { GSM_ClearMultiPartSMSInfo(info); GSM_Debug_Info *debug = GSM_GetGlobalDebug(); /* Copy/convert destination phone number */ char *sms_destination_number = convert_utf8_utf16be(*argp++, FALSE); if (!sms_destination_number) { status.err = "Invalid UTF-8 sequence in destination number"; goto cleanup_end; } string_info_t nsi; utf16be_string_info(sms_destination_number, &nsi); /* Check size of phone number: We'll be decoding this in to a fixed-sized buffer. */ if (nsi.units >= GSM_MAX_NUMBER_LENGTH) { status.err = "Phone number is too long"; goto cleanup_transmit_status; } /* Missing message text: This shouldn't happen since we check `argc` above, but I'm leaving this here in case we refactor later. */ if (*argp == NULL) { status.err = "No message body provided"; goto cleanup_transmit_status; } /* UTF-8 message content */ char *sms_message = *argp++; /* Convert message from UTF-8 to UTF-16-BE: Every symbol is two bytes long; the string is then terminated by a single 2-byte UTF-16 null character. */ char *sms_message_utf16be = convert_utf8_utf16be(sms_message, FALSE); if (!sms_message_utf16be) { status.err = "Invalid UTF-8 sequence"; goto cleanup_transmit_status; } /* Prepare message info structure: This information is used to encode the possibly-multipart SMS. */ info->Class = 1; info->EntriesNum = 1; info->Entries[0].ID = SMS_ConcatenatedTextLong; info->Entries[0].Buffer = (uint8_t *) sms_message_utf16be; info->UnicodeCoding = !utf16be_is_gsm_string(sms_message_utf16be); if ((s->err = GSM_EncodeMultiPartSMS(debug, info, sms)) != ERR_NONE) { status.err = "Failed to encode message"; goto cleanup_sms_text; } status.parts_sent = 0; status.parts_total = sms->Number; /* For each SMS part... */ for (unsigned int i = 0; i < sms->Number; i++) { status.finished = FALSE; status.message_part_index = i; sms->SMS[i].PDU = SMS_Submit; /* Copy destination phone number: This is a fixed-size buffer; size was already checked above. */ CopyUnicodeString(sms->SMS[i].SMSC.Number, smsc->Number); CopyUnicodeString( sms->SMS[i].Number, (unsigned char *) sms_destination_number ); /* Transmit a single message part */ if ((s->err = GSM_SendSMS(s->sm, &sms->SMS[i])) != ERR_NONE) { status.parts[i].err = "Message transmission failed"; continue; } for (;;) { /* Wait for reply */ GSM_ReadDevice(s->sm, TRUE); if (status.finished) { break; } } if (!status.parts[i].transmitted) { status.parts[i].err = "Message delivery failed"; continue; } status.parts_sent++; } cleanup_sms_text: status.message_index = ++message_index; free(sms_message_utf16be); cleanup_transmit_status: print_json_transmit_status(s, sms, &status, is_start); free(sms_destination_number); cleanup_end: is_start = FALSE; } cleanup_sms: free(sms); free(smsc); free(info); printf("]\n"); cleanup: return rv; }
/* Find one multi SMS to sending and return it (or return ERR_EMPTY) * There is also set ID for SMS * File extension convention: * OUTxxxxx.txt : normal text SMS * Options appended to the extension applying to this SMS only: * d: delivery report requested * f: flash SMS * b: WAP bookmark as name,URL * e.g. OUTG20040620_193810_123_+4512345678_xpq.txtdf * is a flash text SMS requesting delivery reports */ static GSM_Error SMSDFiles_FindOutboxSMS(GSM_MultiSMSMessage * sms, GSM_SMSDConfig * Config, char *ID) { GSM_MultiPartSMSInfo SMSInfo; GSM_WAPBookmark Bookmark; char FileName[100], FullName[400]; unsigned char Buffer[(GSM_MAX_SMS_LENGTH * GSM_MAX_MULTI_SMS + 1) * 2]; unsigned char Buffer2[(GSM_MAX_SMS_LENGTH * GSM_MAX_MULTI_SMS + 1) * 2]; FILE *File; int i, len, phlen; char *pos1, *pos2, *options = NULL; gboolean backup = FALSE; #ifdef GSM_ENABLE_BACKUP GSM_SMS_Backup smsbackup; GSM_Error error; #endif #ifdef WIN32 struct _finddata_t c_file; intptr_t hFile; strcpy(FullName, Config->outboxpath); strcat(FullName, "OUT*.txt*"); hFile = _findfirst(FullName, &c_file); if (hFile == -1) { strcpy(FullName, Config->outboxpath); strcat(FullName, "OUT*.smsbackup*"); hFile = _findfirst(FullName, &c_file); backup = TRUE; } if (hFile == -1) { return ERR_EMPTY; } else { strcpy(FileName, c_file.name); } _findclose(hFile); #elif defined(HAVE_DIRBROWSING) struct dirent **namelist = NULL; int cur_file, num_files; char *pos; strcpy(FullName, Config->outboxpath); FullName[strlen(Config->outboxpath) - 1] = '\0'; num_files = scandir(FullName, &namelist, 0, alphasort); for (cur_file = 0; cur_file < num_files; cur_file++) { /* Hidden file or current/parent directory */ if (namelist[cur_file]->d_name[0] == '.') { continue; } /* We care only about files starting with out */ if (strncasecmp(namelist[cur_file]->d_name, "out", 3) != 0) { continue; } /* Check extension */ pos = strrchr(namelist[cur_file]->d_name, '.'); if (pos == NULL) { continue; } if (strncasecmp(pos, ".txt", 4) == 0) { /* We have found text file */ backup = FALSE; break; } if (strncasecmp(pos, ".smsbackup", 10) == 0) { /* We have found a SMS backup file */ backup = TRUE; break; } } /* Remember file name */ if (cur_file < num_files) { strcpy(FileName, namelist[cur_file]->d_name); } /* Free scandir result */ for (i = 0; i < num_files; i++) { free(namelist[i]); } free(namelist); namelist = NULL; /* Did we actually find something? */ if (cur_file >= num_files) { return ERR_EMPTY; } #else return ERR_NOTSUPPORTED; #endif strcpy(FullName, Config->outboxpath); strcat(FullName, FileName); if (backup) { #ifdef GSM_ENABLE_BACKUP /* Remember ID */ strcpy(ID, FileName); /* Load backup */ GSM_ClearSMSBackup(&smsbackup); error = GSM_ReadSMSBackupFile(FullName, &smsbackup); if (error != ERR_NONE) { return error; } /* Copy it to our message */ sms->Number = 0; for (i = 0; smsbackup.SMS[i] != NULL; i++) { sms->SMS[sms->Number++] = *smsbackup.SMS[i]; } /* Free memory */ GSM_FreeSMSBackup(&smsbackup); /* Set delivery report flag */ if (sms->SMS[0].PDU == SMS_Status_Report) { Config->currdeliveryreport = 1; } else { Config->currdeliveryreport = -1; } #else SMSD_Log(DEBUG_ERROR, Config, "SMS backup loading disabled at compile time!"); return ERR_DISABLED; #endif } else { options = strrchr(FileName, '.') + 4; File = fopen(FullName, "rb"); if (File == NULL) { return ERR_CANTOPENFILE; } len = fread(Buffer, 1, sizeof(Buffer) - 2, File); fclose(File); if ((len < 2) || (len >= 2 && ((Buffer[0] != 0xFF || Buffer[1] != 0xFE) && (Buffer[0] != 0xFE || Buffer[1] != 0xFF)))) { if (len > GSM_MAX_SMS_LENGTH * GSM_MAX_MULTI_SMS) len = GSM_MAX_SMS_LENGTH * GSM_MAX_MULTI_SMS; EncodeUnicode(Buffer2, Buffer, len); len = len * 2; memmove(Buffer, Buffer2, len); Buffer[len] = 0; Buffer[len + 1] = 0; } else { Buffer[len] = 0; Buffer[len + 1] = 0; /* Possibly convert byte order */ ReadUnicodeFile(Buffer2, Buffer); } GSM_ClearMultiPartSMSInfo(&SMSInfo); sms->Number = 0; SMSInfo.ReplaceMessage = 0; SMSInfo.Entries[0].Buffer = Buffer2; SMSInfo.Class = -1; SMSInfo.EntriesNum = 1; Config->currdeliveryreport = -1; if (strchr(options, 'd')) Config->currdeliveryreport = 1; if (strchr(options, 'f')) SMSInfo.Class = 0; /* flash SMS */ if (strcasecmp(Config->transmitformat, "unicode") == 0) { SMSInfo.Entries[0].ID = SMS_ConcatenatedTextLong; SMSInfo.UnicodeCoding = TRUE; } else if (strcasecmp(Config->transmitformat, "7bit") == 0) { SMSInfo.Entries[0].ID = SMS_ConcatenatedTextLong; SMSInfo.UnicodeCoding = FALSE; } else { /* auto */ SMSInfo.Entries[0].ID = SMS_ConcatenatedAutoTextLong; } if (strchr(options, 'b')) { // WAP bookmark as title,URL SMSInfo.Entries[0].Buffer = NULL; SMSInfo.Entries[0].Bookmark = &Bookmark; SMSInfo.Entries[0].ID = SMS_NokiaWAPBookmarkLong; SMSInfo.Entries[0].Bookmark->Location = 0; pos2 = mywstrstr(Buffer2, "\0,"); if (pos2 == NULL) { pos2 = Buffer2; } else { *pos2 = '\0'; pos2++; *pos2 = '\0'; pos2++; // replace comma by zero } len = UnicodeLength(Buffer2); if (len > 50) { len = 50; } memmove(&SMSInfo.Entries[0].Bookmark->Title, Buffer2, len * 2); pos1 = &SMSInfo.Entries[0].Bookmark->Title[0] + len * 2; *pos1 = '\0'; pos1++; *pos1 = '\0'; len = UnicodeLength(pos2); if (len > 255) { len = 255; } memmove(&SMSInfo.Entries[0].Bookmark->Address, pos2, len * 2); pos1 = &SMSInfo.Entries[0].Bookmark->Address[0] + len * 2; *pos1 = '\0'; pos1++; *pos1 = '\0'; } GSM_EncodeMultiPartSMS(GSM_GetDebug(Config->gsm), &SMSInfo, sms); strcpy(ID, FileName); pos1 = FileName; for (i = 1; i <= 3 && pos1 != NULL; i++) { pos1 = strchr(++pos1, '_'); } if (pos1 != NULL) { /* OUT<priority><date>_<time>_<serialno>_<phone number>_<anything>.txt */ pos2 = strchr(++pos1, '_'); if (pos2 != NULL) { phlen = strlen(pos1) - strlen(pos2); } else { /* something wrong */ return ERR_UNKNOWN; } } else if (i == 2) { /* OUTxxxxxxx.txt or OUTxxxxxxx */ pos1 = &FileName[3]; pos2 = strchr(pos1, '.'); if (pos2 == NULL) { phlen = strlen(pos1); } else { phlen = strlen(pos1) - strlen(pos2); } } else if (i == 4) { /* OUT<priority>_<phone number>_<serialno>.txt */ pos1 = strchr(FileName, '_'); pos2 = strchr(++pos1, '_'); phlen = strlen(pos1) - strlen(pos2); } else { /* something wrong */ return ERR_UNKNOWN; } for (len = 0; len < sms->Number; len++) { EncodeUnicode(sms->SMS[len].Number, pos1, phlen); } } if (sms->Number != 0) { DecodeUnicode(sms->SMS[0].Number, Buffer); if (options != NULL && strchr(options, 'b')) { // WAP bookmark as title,URL SMSD_Log(DEBUG_NOTICE, Config, "Found %i sms to \"%s\" with bookmark \"%s\" cod %i lgt %i udh: t %i l %i dlr: %i fls: %i", sms->Number, Buffer, DecodeUnicodeString(SMSInfo.Entries[0].Bookmark->Address), sms->SMS[0].Coding, sms->SMS[0].Length, sms->SMS[0].UDH.Type, sms->SMS[0].UDH.Length, Config->currdeliveryreport, SMSInfo.Class); } else { SMSD_Log(DEBUG_NOTICE, Config, "Found %i sms to \"%s\" with text \"%s\" cod %i lgt %i udh: t %i l %i dlr: %i fls: %i", sms->Number, Buffer, DecodeUnicodeString(sms->SMS[0].Text), sms->SMS[0].Coding, sms->SMS[0].Length, sms->SMS[0].UDH.Type, sms->SMS[0].UDH.Length, Config->currdeliveryreport, sms->SMS[0].Class); } } else { SMSD_Log(DEBUG_NOTICE, Config, "error: SMS-count = 0"); } return ERR_NONE; }
/* Class 1 message (normal) */ SMSInfo.Class = 1; /* Message will be consist of one part */ SMSInfo.EntriesNum = 1; /* No unicode */ SMSInfo.UnicodeCoding = FALSE; /* The part has type long text */ SMSInfo.Entries[0].ID = SMS_ConcatenatedTextLong; /* Encode message text */ EncodeUnicode(message_unicode, message_text, strlen(message_text)); SMSInfo.Entries[0].Buffer = message_unicode; printf("%s\n", DecodeUnicodeConsole(SMSInfo.Entries[0].Buffer)); /* Encode message into PDU parts */ error = GSM_EncodeMultiPartSMS(debug_info, &SMSInfo, &SMS); error_handler(); /* Allocates state machine */ s = GSM_AllocStateMachine(); if (s == NULL) return 3; /* * Enable state machine debugging to stderr * Same could be achieved by just using global debug config. */ debug_info = GSM_GetDebug(s); GSM_SetDebugGlobal(FALSE, debug_info); GSM_SetDebugFileDescriptor(stderr, TRUE, debug_info); GSM_SetDebugLevel("textall", debug_info);