Imported gammu 0.90.7
[gammu.git] / gammu / smsd / smsdcore.c
blobb540fe585c747c97226151f7c5f9b1f87935fdb6
2 #include <string.h>
3 #include <signal.h>
4 #include <stdarg.h>
5 #include <time.h>
7 #include "smsdcore.h"
8 #include "../gammu.h"
9 #include "s_files.h"
10 #include "../../common/misc/coding/coding.h"
12 FILE *smsd_log_file = NULL;
13 static GSM_Error SendingSMSStatus;
15 static void SMSSendingSMSStatus (char *Device, int status)
17 dbgprintf("Incoming SMS device: \"%s\" status=%d\n",Device, status);
18 if (status==0) {
19 SendingSMSStatus = GE_NONE;
20 } else {
21 SendingSMSStatus = GE_UNKNOWN;
25 void GSM_Terminate_SMSD(char *msg, int error, bool exitprogram, int rc)
27 int ret = GE_NONE;
29 if (s.opened) {
30 WriteSMSDLog("Terminating communication");
31 ret=GSM_TerminateConnection(&s);
32 if (ret!=GE_NONE) {
33 printf("%s\n",print_error(error,s.di.df,s.msg));
34 if (s.opened) GSM_TerminateConnection(&s);
37 if (error != 0) {
38 WriteSMSDLog(msg, error, print_error(error,s.di.df,s.msg));
39 fprintf(stderr, msg, error, print_error(error,s.di.df,s.msg));
40 fprintf(stderr, "\n");
42 if (exitprogram) {
43 if (smsd_log_file!=NULL) fclose(smsd_log_file);
44 exit(rc);
48 void WriteSMSDLog(unsigned char *format, ...)
50 GSM_DateTime date_time;
51 char Buffer[2000];
52 va_list argp;
53 int result;
55 if (smsd_log_file != NULL) {
56 va_start(argp, format);
57 result = vsprintf(Buffer,GetMsg(s.msg,format),argp);
58 va_end(argp);
60 GSM_GetCurrentDateTime(&date_time);
62 fprintf(smsd_log_file,"%s %4d/%02d/%02d %02d:%02d:%02d : %s\n",
63 DayOfWeek(date_time.Year, date_time.Month, date_time.Day),
64 date_time.Year, date_time.Month, date_time.Day,
65 date_time.Hour, date_time.Minute, date_time.Second,Buffer);
66 fflush(smsd_log_file);
70 void SMSD_ReadConfig(int argc, char *argv[], GSM_SMSDConfig *Config)
72 INI_Section *smsdcfgfile = NULL;
73 GSM_Config smsdcfg;
74 unsigned char *str;
75 static unsigned char emptyPath[1] = "\0";
77 smsdcfgfile=INI_ReadFile(argv[3], false);
78 if (smsdcfgfile==NULL) {
79 fprintf(stderr,"Can't find file \"%s\"\n",argv[3]);
80 exit(-1);
83 Config->logfilename=INI_GetValue(smsdcfgfile, "smsd", "logfile", false);
84 if (Config->logfilename != NULL) {
85 smsd_log_file=fopen(Config->logfilename,"ab");
86 if (smsd_log_file == NULL) {
87 fprintf(stderr,"Can't open file \"%s\"\n",Config->logfilename);
88 exit(-1);
90 fprintf(stderr,"Log filename is \"%s\"\n",Config->logfilename);
92 WriteSMSDLog("Start GAMMU smsd");
94 /* Include Numbers used, because we don't want create new variable */
95 Config->IncludeNumbers=INI_FindLastSectionEntry(smsdcfgfile, "gammu", false);
96 if (Config->IncludeNumbers) {
97 GSM_ReadConfig(smsdcfgfile, &smsdcfg, 0);
98 memcpy(&s.Config,&smsdcfg,sizeof(GSM_Config));
101 Config->PINCode=INI_GetValue(smsdcfgfile, "smsd", "PIN", false);
102 if (Config->PINCode == NULL) {
103 WriteSMSDLog("No PIN code in %s file",argv[3]);
104 fprintf(stderr,"No PIN code in %s file\n",argv[3]);
105 exit(-1);
107 WriteSMSDLog("PIN code is \"%s\"",Config->PINCode);
109 str = INI_GetValue(smsdcfgfile, "smsd", "commtimeout", false);
110 if (str) Config->commtimeout=atoi(str); else Config->commtimeout = 1;
111 str = INI_GetValue(smsdcfgfile, "smsd", "sendtimeout", false);
112 if (str) Config->sendtimeout=atoi(str); else Config->sendtimeout = 10;
113 str = INI_GetValue(smsdcfgfile, "smsd", "receivefrequency", false);
114 if (str) Config->receivefrequency=atoi(str); else Config->receivefrequency = 0;
115 WriteSMSDLog("commtimeout=%i, sendtimeout=%i, receivefrequency=%i", Config->commtimeout, Config->sendtimeout, Config->receivefrequency);
117 Config->deliveryreport = INI_GetValue(smsdcfgfile, "smsd", "deliveryreport", false);
118 if (Config->deliveryreport == NULL || (!mystrncasecmp(Config->deliveryreport, "log", 3) && !mystrncasecmp(Config->deliveryreport, "sms", 3))) {
119 Config->deliveryreport = "no";
121 WriteSMSDLog("deliveryreport = %s", Config->deliveryreport);
123 Config->inboxpath=INI_GetValue(smsdcfgfile, "smsd", "inboxpath", false);
124 if (Config->inboxpath == NULL) Config->inboxpath = emptyPath;
126 Config->inboxformat=INI_GetValue(smsdcfgfile, "smsd", "inboxformat", false);
127 if (Config->inboxformat == NULL || (!mystrncasecmp(Config->inboxformat, "detail", 6) && !mystrncasecmp(Config->inboxformat, "unicode", 7))) {
128 Config->inboxformat = "standard";
130 WriteSMSDLog("Inbox is \"%s\" with format \"%s\"", Config->inboxpath, Config->inboxformat);
132 Config->outboxpath=INI_GetValue(smsdcfgfile, "smsd", "outboxpath", false);
133 if (Config->outboxpath == NULL) Config->outboxpath = emptyPath;
135 Config->transmitformat=INI_GetValue(smsdcfgfile, "smsd", "transmitformat", false);
136 if (Config->transmitformat == NULL || (!mystrncasecmp(Config->transmitformat, "auto", 4) && !mystrncasecmp(Config->transmitformat, "unicode", 7))) {
137 Config->transmitformat = "7bit";
139 WriteSMSDLog("Outbox is \"%s\" with transmission format \"%s\"", Config->outboxpath, Config->transmitformat);
141 Config->sentsmspath=INI_GetValue(smsdcfgfile, "smsd", "sentsmspath", false);
142 if (Config->sentsmspath == NULL) Config->sentsmspath = Config->outboxpath;
143 WriteSMSDLog("Sent SMS moved to \"%s\"",Config->sentsmspath);
145 Config->errorsmspath=INI_GetValue(smsdcfgfile, "smsd", "errorsmspath", false);
146 if (Config->errorsmspath == NULL) Config->errorsmspath = Config->sentsmspath;
147 WriteSMSDLog("SMS with errors moved to \"%s\"",Config->errorsmspath);
149 Config->IncludeNumbers=INI_FindLastSectionEntry(smsdcfgfile, "include_numbers", false);
150 Config->ExcludeNumbers=INI_FindLastSectionEntry(smsdcfgfile, "exclude_numbers", false);
151 if (Config->IncludeNumbers != NULL) {
152 WriteSMSDLog("Include numbers available");
154 if (Config->ExcludeNumbers != NULL) {
155 if (Config->IncludeNumbers == NULL) {
156 WriteSMSDLog("Exclude numbers available");
157 } else {
158 WriteSMSDLog("Exclude numbers available, but IGNORED");
162 Config->retries = 0;
163 Config->prevSMSID[0] = 0;
166 bool SMSD_CheckSecurity(GSM_SMSDConfig *Config)
168 GSM_SecurityCode SecurityCode;
169 GSM_Error error;
171 /* Need PIN ? */
172 error=Phone->GetSecurityStatus(&s,&SecurityCode.Type);
173 /* Unknown error */
174 if (error != GE_NOTSUPPORTED && error != GE_NONE) {
175 WriteSMSDLog("Error getting security status (%i)", error);
176 return false;
178 /* No supported - do not check more */
179 if (error == GE_NOTSUPPORTED) return true;
180 /* If PIN, try to enter */
181 switch (SecurityCode.Type) {
182 case GSCT_Pin:
183 WriteSMSDLog("Trying to enter PIN");
184 strcpy(SecurityCode.Code,Config->PINCode);
185 error=Phone->EnterSecurityCode(&s,SecurityCode);
186 if (error == GE_SECURITYERROR) {
187 GSM_Terminate_SMSD("ERROR: incorrect PIN", error, true, -1);
189 if (error != GE_NONE) {
190 WriteSMSDLog("Error entering PIN (%i)", error);
191 return false;
193 break;
194 case GSCT_SecurityCode:
195 case GSCT_Pin2:
196 case GSCT_Puk:
197 case GSCT_Puk2:
198 GSM_Terminate_SMSD("ERROR: phone requires not supported code type", 0, true, -1);
199 case GSCT_None:
200 break;
202 return true;
205 bool SMSD_ReadDeleteSMS(GSM_SMSDConfig *Config, GSM_SMSDService *Service)
207 bool start,process;
208 GSM_MultiSMSMessage sms;
209 unsigned char buffer[100];
210 GSM_Error error=GE_NONE;
211 INI_Entry *e;
212 int i;
214 start=true;
215 while (error == GE_NONE && !bshutdown) {
216 sms.SMS[0].Folder=0x00;
217 error=Phone->GetNextSMS(&s, &sms, start);
218 switch (error) {
219 case GE_EMPTY:
220 break;
221 case GE_NONE:
222 /* Not Inbox SMS - exit */
223 if (!sms.SMS[0].InboxFolder) break;
224 process=true;
225 DecodeUnicode(sms.SMS[0].Number,buffer);
226 if (Config->IncludeNumbers != NULL) {
227 e=Config->IncludeNumbers;
228 process=false;
229 while (1) {
230 if (e == NULL) break;
231 if (strcmp(buffer,e->EntryValue)==0) {
232 process=true;
233 break;
235 e = e->Prev;
237 } else if (Config->ExcludeNumbers != NULL) {
238 e=Config->ExcludeNumbers;
239 process=true;
240 while (1) {
241 if (e == NULL) break;
242 if (strcmp(buffer,e->EntryValue)==0) {
243 process=false;
244 break;
246 e = e->Prev;
249 if (process) {
250 Service->SaveInboxSMS(sms, Config);
251 } else {
252 WriteSMSDLog("Excluded %s", buffer);
254 break;
255 default:
256 WriteSMSDLog("Error getting SMS (%i)", error);
257 return false;
259 if (error == GE_NONE && sms.SMS[0].InboxFolder) {
260 for (i=0;i<sms.Number;i++) {
261 sms.SMS[i].Folder=0;
262 error=Phone->DeleteSMS(&s,&sms.SMS[i]);
263 switch (error) {
264 case GE_NONE:
265 case GE_EMPTY:
266 break;
267 default:
268 WriteSMSDLog("Error deleting SMS (%i)", error);
269 return false;
273 start=false;
275 return true;
278 bool SMSD_CheckSMSStatus(GSM_SMSDConfig *Config,GSM_SMSDService *Service)
280 GSM_SMSMemoryStatus SMSStatus;
281 GSM_Error error;
283 /* Do we have any SMS in phone ? */
284 error=Phone->GetSMSStatus(&s,&SMSStatus);
285 if (error != GE_NONE) {
286 WriteSMSDLog("Error getting SMS status (%i)", error);
287 return false;
289 /* Yes. We have SMS in phone */
290 if (SMSStatus.SIMUsed+SMSStatus.PhoneUsed != 0) {
291 return SMSD_ReadDeleteSMS(Config,Service);
293 return true;
296 bool SMSD_SendSMS(GSM_SMSDConfig *Config,GSM_SMSDService *Service)
298 GSM_MultiSMSMessage sms;
299 GSM_DateTime Date;
300 GSM_Error error;
301 unsigned int i, j, z;
303 error = Service->FindOutboxSMS(&sms, Config, Config->SMSID);
305 if (error == GE_EMPTY) {
306 /* No outbox sms - wait few seconds and escape */
307 for (j=0;j<Config->commtimeout && !bshutdown;j++) {
308 GSM_GetCurrentDateTime (&Date);
309 i=Date.Second;
310 while (i==Date.Second && !bshutdown) {
311 my_sleep(10);
312 GSM_GetCurrentDateTime(&Date);
315 return true;
317 if (error != GE_NONE) {
318 /* Unknown error - escape */
319 WriteSMSDLog("Error in outbox on %s", Config->SMSID);
320 Service->MoveSMS(Config->outboxpath, Config->errorsmspath, Config->SMSID, true);
321 return false;
323 if (!bshutdown) {
324 if (strcmp(Config->prevSMSID, Config->SMSID) == 0) {
325 Config->retries++;
326 if (Config->retries > MAX_RETRIES) {
327 Config->retries = 0;
328 strcpy(Config->prevSMSID, "");
329 WriteSMSDLog("Moved to errorbox: %s", Config->SMSID);
330 Service->MoveSMS(Config->outboxpath, Config->errorsmspath, Config->SMSID, true);
331 return false;
333 } else {
334 Config->retries = 0;
335 strcpy(Config->prevSMSID, Config->SMSID);
337 for (i=0;i<sms.Number;i++) {
338 if (strcmp(Config->deliveryreport, "no") != 0) sms.SMS[i].PDU = SMS_Status_Report;
339 error=Phone->SendSMS(&s, &sms.SMS[i]);
340 if (error!=GE_NONE) {
341 WriteSMSDLog("Error sending SMS %s (%i): %s", Config->SMSID, error,print_error(error,s.di.df,s.msg));
342 return false;
344 j=0;
345 SendingSMSStatus = GE_TIMEOUT;
346 while (!bshutdown) {
347 GSM_GetCurrentDateTime (&Date);
348 z=Date.Second;
349 while (z==Date.Second) {
350 my_sleep(10);
351 GSM_GetCurrentDateTime(&Date);
352 GSM_ReadDevice(&s,true);
353 if (SendingSMSStatus != GE_TIMEOUT) break;
355 if (SendingSMSStatus != GE_TIMEOUT) break;
356 j++;
357 if (j>Config->sendtimeout) break;
359 if (SendingSMSStatus != GE_NONE) {
360 WriteSMSDLog("Error getting send status of %s (%i): %s", Config->SMSID, SendingSMSStatus,print_error(SendingSMSStatus,s.di.df,s.msg));
361 return false;
363 WriteSMSDLog("Transmitted %s (%s: %i) to %s", Config->SMSID, (i+1 == sms.Number?"total":"part"),i+1,DecodeUnicodeString(sms.SMS[0].Number));
365 strcpy(Config->prevSMSID, "");
366 if (Service->MoveSMS(Config->outboxpath, Config->sentsmspath, Config->SMSID, false) != GE_NONE)
367 Service->MoveSMS(Config->outboxpath, Config->errorsmspath, Config->SMSID, true);
369 return true;
372 void SMSDaemon(int argc, char *argv[])
374 int errors = 255, initerrors=0;
375 GSM_SMSDService *Service;
376 GSM_Error error;
377 time_t time1;
378 GSM_SMSDConfig Config;
380 if (!strcmp(argv[2],"FILES")) {
381 Service = &SMSDFiles;
382 } else {
383 fprintf(stderr,"Unknown service type (\"%s\")\n",argv[2]);
384 exit(-1);
387 SMSD_ReadConfig(argc, argv, &Config);
389 error = Service->Init();
390 if (error!=GE_NONE) {
391 GSM_Terminate_SMSD("Stop GAMMU smsd (%i)", error, true, -1);
394 /* We do not want to monitor serial line forever -
395 * press Ctrl+C to stop the monitoring mode.
397 signal(SIGINT, interrupted);
398 signal(SIGTERM, interrupted);
399 fprintf(stderr,"Press Ctrl+C to stop the program ...\n");
401 time1 = time(NULL);
402 SendingSMSStatus = GE_UNKNOWN;
404 /* Loop here indefinitely -
405 * allows you to see messages from GSM code in
406 * response to unknown messages etc.
407 * The loops ends after pressing the Ctrl+C.
409 while (!bshutdown) {
410 /* There were errors in communication - try to recover */
411 if (errors > 2) {
412 if (errors != 255) {
413 WriteSMSDLog("Terminating communication (%i,%i)", error, errors);
414 error=GSM_TerminateConnection(&s);
416 if (initerrors++ > 3) my_sleep(30000);
417 WriteSMSDLog("Starting communication");
418 error=GSM_InitConnection(&s,2);
419 switch (error) {
420 case GE_NONE:
421 s.User.SendSMSStatus = SMSSendingSMSStatus;
422 Phone = s.Phone.Functions;
423 errors = 0;
424 /* Marcin Wiacek: FIXME. To check */
425 // di = s.di;
426 break;
427 case GE_DEVICEOPENERROR:
428 GSM_Terminate_SMSD("Can't open device (%i)", error, true, -1);
429 default:
430 WriteSMSDLog("Error at init connection (%i)", error);
431 errors = 250;
433 continue;
435 if ((difftime(time(NULL), time1) >= Config.receivefrequency) || (SendingSMSStatus != GE_NONE)) {
436 time1 = time(NULL);
438 if (!SMSD_CheckSecurity(&Config))
440 errors++;
441 initerrors++;
442 continue;
443 } else errors=0;
445 initerrors = 0;
447 if (!SMSD_CheckSMSStatus(&Config,Service))
449 errors++;
450 continue;
451 } else errors=0;
453 if (!SMSD_SendSMS(&Config,Service)) continue;
455 GSM_Terminate_SMSD("Stop GAMMU smsd", 0, false, 0);
458 /* How should editor hadle tabs in this file? Add editor commands here.
459 * vim: noexpandtab sw=8 ts=8 sts=8: