Implemented connection type "tcp" (GCT_TCP), use <hostname>:<port> as "port"
[gnokii.git] / common / cimd.c
1 /*
2
3   $Id$
4   
5   G N O K I I
6
7   A Linux/Unix toolset and driver for Nokia mobile phones.
8
9   Copyright (C) 2001 Jan Kratochvil,
10   based on code by Hugh Blemings & Pavel Janík ml.
11
12   Released under the terms of the GNU GPL, see file COPYING for more details.
13
14   This file provides an API for accessing SMS centers by CIMD and related protococols.
15   See README-CIMD for more details on supported protocols.
16
17   The various routines are prefixed by CIMD.
18
19   $Log$
20   Revision 1.1.1.2  2002/04/03 01:44:15  short
21   Implemented connection type "tcp" (GCT_TCP), use <hostname>:<port> as "port"
22
23   Revision 1.1.1.1  2002/04/03 00:08:03  short
24   Found in "gnokii-working" directory, some November-patches version
25
26
27 */
28
29 #define CIMD_DEBUG 1
30
31 /* System header files */
32
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <stdarg.h>
37 #include <limits.h>
38  
39
40 #ifdef WIN32
41
42 #include <windows.h>
43 #include "win32/winserial.h"
44
45 #undef IN
46 #undef OUT
47
48 #define WRITEPHONE(a, b, c) WriteCommBlock(b, c)
49 #define sleep(x) Sleep((x) * 1000)
50 #define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000))
51 extern HANDLE hPhone;
52
53 #else
54
55 #define WRITEPHONE(a, b, c) device_write(b, c)
56 #include <unistd.h>
57 #include <termios.h>
58 #include <fcntl.h>
59 #include <ctype.h>
60 #include <signal.h>
61 #include <sys/ioctl.h>
62 #include <sys/types.h>
63 #include <sys/time.h>
64 #include <pthread.h>
65 #include <errno.h>
66 #include "device.h"
67 #include "devices/unixserial.h"
68
69 #endif
70
71 /* Various header file */
72
73 #include "config.h"
74 #include "misc.h"
75 #include "gsm-common.h"
76 #include "cfgreader.h"
77
78 /* Global variables used by code in gsm-api.c to expose the functions
79    supported by this model of phone. */
80
81
82 #if __unices__
83 /* fd opened in device.c */
84 extern int device_portfd;
85 #endif
86
87 /* Our private defines */
88
89 /* Define if plain (non-Bin) Submit is needed for 7bit messages
90  */
91 #define CIMD_SUBMIT_7BIT 1
92
93 /* When now catchbuffer was provided and must have some space to decode
94  * OK/ERROR/... result codes.
95  */
96 #define CIMD_CATCHBUFFER_LENGTH 0x400
97
98 /* +1 of maximum position number of CIMD protocol parameter */
99 #define CIMD_PARAMSLOTS (0x10)
100
101 /* We assume the 'right one' class is 1 (Mobile Equipment specific) */
102 #define DEFAULT_CLASS 1
103
104 /* CIMD_Param_Nak_Error codes:
105  */
106 #define CIMD_NAK_NO_SMS (0x5001)
107 #define CIMD_NAK_KEEPALIVE_REPLY (0x9998)
108 #define CIMD_NAK_RESEND (0x9999)
109
110 /* Local variables */
111
112 #ifndef WIN32
113 static char PortDevice[GSM_MAX_DEVICE_NAME_LENGTH];
114 #endif
115 static bool RequestTerminate;
116
117
118 static u8 CIMD_CatchBuffer[CIMD_CATCHBUFFER_LENGTH];
119 static u8 *CIMD_CatchBufferPtr=CIMD_CatchBuffer;        /* current destination writing ptr */
120 static GSM_Error *CIMD_CatchBufferErrorP;
121
122 static void CIMD_CatchBufferStart(GSM_Error *errorcodep);
123
124
125 #define CIMD_MARK_START (0x02)
126 #define CIMD_MARK_STOP  (0x03)
127 #define CIMD_MARK_SEP   (0x09)
128 #define CIMD_MARK_SEPS  "\x09"
129
130 /* ELF=ELement Format */
131 enum CIMD_ELF {
132         CIMD_ELF_HexByte,
133         CIMD_ELF_HexWord,
134         CIMD_ELF_Decimal,
135         CIMD_ELF_StringZ,
136         CIMD_ELF_HexBlock,
137         CIMD_ELF_Empty,
138         };
139
140 struct CIMD_Param {
141         u16 code;
142         enum CIMD_ELF elf;
143         };
144
145 enum CIMD_Param_SAMPLE {                /* we need some universal enum for typecasting all params */
146         CIMD_Param_SAMPLE_dummy,
147         };
148
149 /* CIMD_Cmd_Ack:
150  */
151 enum CIMD_Param_Ack {
152         CIMD_Param_Ack_Cmd,
153         CIMD_Param_Ack_Error,
154         CIMD_Param_Ack_NULL
155         };
156 static const struct CIMD_Param CIMD_Param_BIP_Ack[]={
157         { /* CIMD_Param_Ack_Cmd   */ 1,CIMD_ELF_HexByte },
158         { /* CIMD_Param_Ack_Error */ 2,CIMD_ELF_StringZ },      /* protocol version on Cmd_Login, otherwise 0x0000 */
159         };
160
161 /* CIMD_Cmd_Nak:
162  */
163 enum CIMD_Param_Nak {
164         CIMD_Param_Nak_Cmd,
165         CIMD_Param_Nak_Error,
166         CIMD_Param_Nak_NULL
167         };
168 static const struct CIMD_Param CIMD_Param_BIP_Nak[]={
169         { /* CIMD_Param_Nak_Cmd   */ 1,CIMD_ELF_HexByte },
170         { /* CIMD_Param_Nak_Error */ 2,CIMD_ELF_HexWord },
171         };
172
173 /* CIMD_Cmd_Login:
174  */
175 enum CIMD_Param_Login {
176         CIMD_Param_Login_ID,
177         CIMD_Param_Login_PWD,
178         CIMD_Param_Login_NULL
179         };
180 static const struct CIMD_Param CIMD_Param_BIP_Login[]={
181         { /* CIMD_Param_Login_ID  */ 1,CIMD_ELF_StringZ },
182         { /* CIMD_Param_Login_PWD */ 2,CIMD_ELF_StringZ },
183         };
184
185 /* CIMD_Cmd_Logout:
186  */
187 enum CIMD_Param_Logout {
188         CIMD_Param_Logout_NULL
189         };
190 static const struct CIMD_Param CIMD_Param_BIP_Logout[]={
191         };
192
193 /* CIMD_Cmd_Retrieve:
194  */
195 enum CIMD_Param_Retrieve {
196         CIMD_Param_Retrieve_NULL
197         };
198 static const struct CIMD_Param CIMD_Param_BIP_Retrieve[]={
199         };
200
201 /* CIMD_Cmd_RetrieveReply:
202  */
203 enum CIMD_Param_RetrieveReply {
204         CIMD_Param_RetrieveReply_Destination,
205         CIMD_Param_RetrieveReply_SourceApplication,     /* ??? */
206         CIMD_Param_RetrieveReply_Text,
207         CIMD_Param_RetrieveReply_Timestamp,
208         CIMD_Param_RetrieveReply_Is8bit,                /* protocol version 1.14+, CIMD_Param_DCSEnable_Type_*DCS* reqd */
209         CIMD_Param_RetrieveReply_PID_DCS,               /* protocol version 1.15+, CIMD_Param_DCSEnable_Type_*PID_DCS* reqd */
210         CIMD_Param_RetrieveReply_SPEC,                  /* protocol version 1.16+, CIMD_Param_DCSEnable_Type_*SPEC* reqd */
211         CIMD_Param_RetrieveReply_NULL
212         };
213 static const struct CIMD_Param CIMD_Param_BIP_RetrieveReply[]={
214         { /* CIMD_Param_RetrieveReply_Destination       */ 1,CIMD_ELF_StringZ },
215         { /* CIMD_Param_RetrieveReply_SourceApplication */ 2,CIMD_ELF_StringZ },
216         { /* CIMD_Param_RetrieveReply_Text              */ 3,CIMD_ELF_StringZ },
217         { /* CIMD_Param_RetrieveReply_Timestamp         */ 4,CIMD_ELF_StringZ },
218         { /* CIMD_Param_RetrieveReply_Is8bit            */ 5,CIMD_ELF_Decimal },
219         { /* CIMD_Param_RetrieveReply_PID_DCS           */ 6,CIMD_ELF_Decimal }, /* upper=PID, lower=DCS, FIXME: should be HexWord! */
220         { /* CIMD_Param_RetrieveReply_SPEC              */ 7,CIMD_ELF_Decimal }, /* FIXME: should be HexByte! */
221         };
222
223 /* CIMD_Cmd_Count:
224  */
225 enum CIMD_Param_Count {
226         CIMD_Param_Count_NULL
227         };
228 static const struct CIMD_Param CIMD_Param_BIP_Count[]={
229         };
230
231 /* CIMD_Cmd_CountReply:
232  */
233 enum CIMD_Param_CountReply {
234         CIMD_Param_CountReply_Used,             /* Used==NotRead==Slots */
235         CIMD_Param_CountReply_NULL
236         };
237 static const struct CIMD_Param CIMD_Param_BIP_CountReply[]={
238         { /* CIMD_Param_CountReply_Used */ 1,CIMD_ELF_Decimal },
239         };
240
241 /* CIMD_Cmd_Submit:
242  */
243 enum CIMD_Param_Submit {
244         CIMD_Param_Submit_Destination,
245         CIMD_Param_Submit_Text,
246         CIMD_Param_Submit_ValidityPeriod,
247         CIMD_Param_Submit_AUX,          /* protocol version 1.13+, empty for BMG<->SMSC link CIMD (just for OIS) */
248         CIMD_Param_Submit_DCS,          /* protocol version 1.14+ */
249         CIMD_Param_Submit_PID,          /* protocol version 1.15+ */
250         CIMD_Param_Submit_SPEC,         /* protocol version 1.16+ */
251         CIMD_Param_Submit_NULL
252         };
253 static const struct CIMD_Param CIMD_Param_BIP_Submit[]={
254         { /* CIMD_Param_Submit_Destination    */ 1,CIMD_ELF_StringZ },
255         { /* CIMD_Param_Submit_Text           */ 2,CIMD_ELF_StringZ },
256         { /* CIMD_Param_Submit_ValidityPeriod */ 3,CIMD_ELF_HexByte },
257         { /* CIMD_Param_Submit_AUX            */ 4,CIMD_ELF_Empty   },
258         { /* CIMD_Param_Submit_DCS            */ 5,CIMD_ELF_Decimal },
259         { /* CIMD_Param_Submit_PID            */ 6,CIMD_ELF_HexByte },
260         { /* CIMD_Param_Submit_SPEC           */ 7,CIMD_ELF_HexByte },
261         };
262
263 /* CIMD_Cmd_SubmitBin:          protocol version 1.12+
264  */
265 enum CIMD_Param_SubmitBin {
266         CIMD_Param_SubmitBin_Destination,
267         CIMD_Param_SubmitBin_Text,
268         CIMD_Param_SubmitBin_ValidityPeriod,
269         CIMD_Param_SubmitBin_AUX,               /* protocol version 1.13+, empty for BMG<->SMSC link CIMD (just for OIS) */
270         CIMD_Param_SubmitBin_DCS,               /* protocol version 1.14+ */
271         CIMD_Param_SubmitBin_PID,               /* protocol version 1.15+ */
272         CIMD_Param_SubmitBin_SPEC,              /* protocol version 1.16+ */
273         CIMD_Param_SubmitBin_NULL
274         };
275 static const struct CIMD_Param CIMD_Param_BIP_SubmitBin[]={
276         { /* CIMD_Param_SubmitBin_Destination    */ 1,CIMD_ELF_StringZ },
277         { /* CIMD_Param_SubmitBin_Text           */ 2,CIMD_ELF_StringZ },
278         { /* CIMD_Param_SubmitBin_ValidityPeriod */ 3,CIMD_ELF_HexByte },
279         { /* CIMD_Param_SubmitBin_AUX            */ 4,CIMD_ELF_Empty   },
280         { /* CIMD_Param_SubmitBin_DCS            */ 5,CIMD_ELF_Decimal },
281         { /* CIMD_Param_SubmitBin_PID            */ 6,CIMD_ELF_HexByte },
282         { /* CIMD_Param_SubmitBin_SPEC           */ 7,CIMD_ELF_HexByte },
283         };
284
285 /* CIMD_Cmd_DCSEnable:          protocol version 1.14+
286  */
287 enum CIMD_Param_DCSEnable {
288         CIMD_Param_DCSEnable_Type,
289         CIMD_Param_DCSEnable_NULL
290         };
291 static const struct CIMD_Param CIMD_Param_BIP_DCSEnable[]={
292         { /* CIMD_Param_DCSEnable_Type */ 1,CIMD_ELF_HexWord },
293         };
294
295 enum CIMD_Param_DCSEnable_Type {
296         CIMD_Param_DCSEnable_Type_None        =0x0000,  /* protocol version 1.14+ */
297         CIMD_Param_DCSEnable_Type_DCS         =0x0001,  /* protocol version 1.14+ */
298         CIMD_Param_DCSEnable_Type_PID_DCS     =0x0002,  /* protocol version 1.15+ */
299         CIMD_Param_DCSEnable_Type_PID_DCS_SPEC=0x0003,  /* protocol version 1.16+ */
300         };
301
302 #define CIMD_PARAM_ENTRY(param) (param),ARRAY_LEN((param))
303
304 struct CIMD_Cmd {
305         u8 code;
306         const struct CIMD_Param *param;
307         unsigned paramcnt;
308         };
309
310 enum CIMD_Cmd_Type {
311         CIMD_Cmd_Ack,
312         CIMD_Cmd_Nak,
313         CIMD_Cmd_Login,
314         CIMD_Cmd_Logout,
315         CIMD_Cmd_Submit,
316         CIMD_Cmd_Retrieve,
317         CIMD_Cmd_RetrieveReply,
318         CIMD_Cmd_Count,
319         CIMD_Cmd_CountReply,
320         CIMD_Cmd_SubmitBin,             /* protocol version 1.12+ */
321         CIMD_Cmd_DCSEnable,             /* protocol version 1.14+ */
322         CIMD_Cmd_NULL           /* stdarg termination, MUST be last! */
323         };
324 static const struct CIMD_Cmd CIMD_Cmd_BIP[]={
325         { /* CIMD_Cmd_Ack           */ 0x00,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Ack          ) },
326         { /* CIMD_Cmd_Nak           */ 0x99,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Nak          ) },
327         { /* CIMD_Cmd_Login         */ 0x01,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Login        ) },
328         { /* CIMD_Cmd_Logout        */ 0x02,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Logout       ) },
329         { /* CIMD_Cmd_Submit        */ 0x03,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Submit       ) },
330         { /* CIMD_Cmd_Retrieve      */ 0x05,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Retrieve     ) },
331         { /* CIMD_Cmd_RetrieveReply */ 0x06,CIMD_PARAM_ENTRY(CIMD_Param_BIP_RetrieveReply) },
332         { /* CIMD_Cmd_Count         */ 0x51,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Count        ) },
333         { /* CIMD_Cmd_CountReply    */ 0x61,CIMD_PARAM_ENTRY(CIMD_Param_BIP_CountReply   ) },
334         { /* CIMD_Cmd_SubmitBin     */ 0x31,CIMD_PARAM_ENTRY(CIMD_Param_BIP_SubmitBin    ) },
335         { /* CIMD_Cmd_DCSEnable     */ 0x53,CIMD_PARAM_ENTRY(CIMD_Param_BIP_DCSEnable    ) },
336         };
337
338 static const struct CIMD_Cmd *CIMD_Cmd=CIMD_Cmd_BIP;    /* FIXME: When other protocols get supported... */
339 static unsigned CIMD_Cmdcnt=ARRAY_LEN(CIMD_Cmd_BIP);    /* FIXME: When other protocols get supported... */
340
341 struct CIMD_Param_Ack_Nak_Error {
342         u16 code;
343         const char *msg;
344         };
345
346 static const struct CIMD_Param_Ack_Nak_Error CIMD_Param_Ack_Nak_Error[]={
347         /* CIMD_Cmd_Login */
348         { 0x1101,N_("User is already logged in") },
349         { 0x1001,N_("Logging in is currently disabled") },
350         { 0x9999,N_("Username not found") },
351                 /* 0x000? can be bitmask combined from: */
352         { 0x0001,N_("Invalid password") },
353         { 0x0002,N_("User has forbidden access") },
354         { 0x0008,N_("User is already registered") },            /* ??? difference from 0x1101 ? */
355         /* CIMD_Cmd_Retrieve / CIMD_Cmd_Count */
356         { 0x5001,N_("No SMS found or not detectable") },        /* ==CIMD_NAK_NO_SMS */
357         { 0x5011,N_("Function not enabled") },
358         /* CIMD_Cmd_Retrieve */
359         { 0x5021,N_("Automatical retrieve is active") },
360         /* CIMD_Cmd_Submit / CIMD_Cmd_SubmitBin */
361         { 0x3001,N_("Error during processing SMS in BMG") },
362         { 0x3061,N_("Destination number has invalid format") },
363         /* CIMD_Cmd_SubmitBin */
364         { 0x3051,N_("Invalid DCS") },           /* protocol version 1.14+ */
365         { 0x3081,N_("Invalid Text") },          /* protocol version 1.14+ */
366         { 0x3091,N_("SMS is empty") },          /* protocol version 1.14+ */
367         { 0x3099,N_("SMS submit too fast") },   /* protocol version 1.16+ */
368         };
369
370
371 static struct CIMD_paramslot CIMD_RX_Packet_slot[CIMD_PARAMSLOTS];
372 #define CIMD_RX_PACKET_PARAMSLOT(paramsample) (CIMD_RX_Packet_slot+1+(unsigned)(paramsample))
373 static enum CIMD_Cmd_Type CIMD_RX_Packet_Cmd;
374 static unsigned CIMD_RX_Packet_slots;
375
376
377 /* RETURNS: Processed */
378 typedef bool (*CIMD_RX_PatrolFunc)(void);
379 typedef void (*CIMD_RX_PatrolReset)(void);
380
381 struct CIMD_RX_Patrol {
382         enum CIMD_Cmd_Type cmd;
383         CIMD_RX_PatrolFunc func;
384         CIMD_RX_PatrolReset reset;
385         };
386
387 static const struct CIMD_RX_Patrol *CIMD_RX_Patrol_Current;
388
389 static char *CIMD_RX_Patrol_Ack_Error;
390 static u16 CIMD_RX_Patrol_Nak_Error;
391
392
393 #ifndef WIN32
394
395 static pthread_t Thread;
396 # if __unices__
397 static pthread_t selThread;
398 # endif
399
400 #endif
401
402 /* Local variables used by get/set phonebook entry code. Buffer is used as a
403    source or destination for phonebook data and other functions... Error is
404    set to GE_NONE by calling function, set to GE_COMPLETE or an error code by
405    handler routines as appropriate. */
406
407 static GSM_SMSMessage     *CurrentSMSMessage;
408 static GSM_Error          CurrentSMSMessageError;
409
410 static GSM_SMSStatus      *CurrentSMSStatus;
411 static GSM_Error          CurrentSMSStatusError;
412
413 static unsigned char      Revision[GSM_MAX_REVISION_LENGTH];
414 static unsigned char      Model   [GSM_MAX_MODEL_LENGTH];
415
416
417 static u8 CIMD_TX_SendCommand_buf[sizeof(CIMD_CatchBuffer)];    /* sizeof() is not required to be == but it is appropriate */
418 static u8 *CIMD_TX_SendCommand_buf_d=CIMD_TX_SendCommand_buf;
419
420 struct CIMD_paramslot {
421         u16 code;
422         enum CIMD_ELF elf;
423         union {
424                 struct { u8    i;               } HexByte;
425                 struct { u16   i;               } HexWord;
426                 struct { int   i;               } Decimal;
427                 struct { char *s;               } StringZ;
428                 struct { u8   *buf; size_t len; } HexBlock;
429                 struct { int _dummy;            } Empty;
430                 } u;
431         };
432
433 /* slots are sorted by moving the whole blocks but they are small so it is OK
434  */
435 static struct CIMD_paramslot CIMD_TX_SendCommand_paramslots[CIMD_PARAMSLOTS];
436 static int CIMD_TX_SendCommand_paramslots_full;
437 static enum CIMD_Cmd_Type CIMD_TX_SendCommand_Cmd;
438
439 /* RETURNS: Success
440  */
441 static bool CIMD_TX_SendCommand_vpushparam(u16 code,enum CIMD_ELF elf,va_list *app)
442 {
443 struct CIMD_paramslot *slot=CIMD_TX_SendCommand_paramslots+(CIMD_TX_SendCommand_paramslots_full++);
444
445         if (slot>=CIMD_TX_SendCommand_paramslots+ARRAY_LEN(CIMD_TX_SendCommand_paramslots)) {
446                 /* assertion */
447                 fprintf(stderr,"Out of param slots!\n");
448                 return(false);
449                 }
450         slot->code=code;
451         slot->elf=elf;
452         switch (elf) {
453                 case CIMD_ELF_HexByte:
454                         slot->u.HexByte.i=va_arg((*app),int);
455                         break;
456                 case CIMD_ELF_HexWord:
457                         slot->u.HexWord.i=va_arg((*app),int);
458                         break;
459                 case CIMD_ELF_Decimal:
460                         slot->u.Decimal.i=va_arg((*app),int);
461                         break;
462                 case CIMD_ELF_StringZ:
463                         slot->u.StringZ.s=va_arg((*app),char *);
464                         break;
465                 case CIMD_ELF_HexBlock:
466                         slot->u.HexBlock.buf=va_arg((*app),u8 *);
467                         slot->u.HexBlock.len=va_arg((*app),size_t);
468                         break;
469                 case CIMD_ELF_Empty:
470                         break;
471                 }
472         return(true);
473 }
474
475 /* RETURNS: Success
476  */
477 static bool CIMD_TX_SendCommand_pushparam(u16 code,enum CIMD_ELF elf,...)
478 {
479 va_list ap;
480 bool r;
481
482         va_start(ap,elf);
483         r=CIMD_TX_SendCommand_vpushparam(code,elf,&ap);
484         va_end(ap);
485         return(r);
486 }
487
488 static int CIMD_TX_SendCommand_paramslots_compare
489         (const struct CIMD_paramslot *a,const struct CIMD_paramslot *b)
490 {
491         return((b->code<a->code)-(a->code<b->code));
492 }
493
494 /* RETURNS: Success
495  */
496 static bool CIMD_TX_SendCommand_storeparam(struct CIMD_paramslot *slot)
497 {
498         switch (slot->elf) {
499                 case CIMD_ELF_HexByte:
500                         CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%02X",slot->u.HexByte.i);
501                         break;
502                 case CIMD_ELF_HexWord:
503                         CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%04X",slot->u.HexWord.i);
504                         break;
505                 case CIMD_ELF_Decimal:
506                         CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%d",slot->u.Decimal.i);
507                         break;
508                 case CIMD_ELF_StringZ: {
509 size_t len;
510                         if (!slot->u.StringZ.s)         /* NULL is interpreted as "" */
511                                 break;
512                         len=strlen(slot->u.StringZ.s);
513                         if (CIMD_TX_SendCommand_buf_d+len > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf))
514                                 return(false);          /* error - overflow */
515                         memcpy(CIMD_TX_SendCommand_buf_d,slot->u.StringZ.s,len);
516                         CIMD_TX_SendCommand_buf_d+=len;
517                         } break;
518                 case CIMD_ELF_HexBlock:
519                         if (CIMD_TX_SendCommand_buf_d+2*slot->u.HexBlock.len
520                                         > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf))
521                                 return(false);          /* error - overflow */
522                         CIMD_TX_SendCommand_buf_d=SMS_BlockToHex(CIMD_TX_SendCommand_buf_d,
523                                         slot->u.HexBlock.buf,slot->u.HexBlock.len);     /* never fails */
524                         break;
525                 case CIMD_ELF_Empty:
526                         break;
527                 }
528         return(true);   /* success */
529 }
530
531 static unsigned CIMD_KeepAlives=0;
532 static pthread_mutex_t CIMD_KeepAlivesLock;
533
534 /* This function is NOT thread-safe!
535  */
536 static void CIMD_KeepAlivesLockInit(void)
537 {
538 static bool done=false;
539
540         if (done)
541                 return;
542         pthread_mutex_init(&CIMD_KeepAlivesLock,NULL);
543         done=true;
544 }
545
546 static void CIMD_TX_SendCommand(GSM_Error *errorcodep,const struct CIMD_RX_Patrol *patrol,enum CIMD_Cmd_Type cmd,...);
547
548 static void CIMD_KeepAlivesCheck(void)
549 {
550 unsigned alives;
551
552         CIMD_KeepAlivesLockInit();
553         pthread_mutex_lock(&CIMD_KeepAlivesLock);
554         alives=CIMD_KeepAlives;
555         CIMD_KeepAlives=0;
556         pthread_mutex_unlock(&CIMD_KeepAlivesLock);
557
558         while (alives) {
559                 CIMD_TX_SendCommand(NULL/*errorcodep*/,NULL/*patrol*/,
560                         CIMD_Cmd_Nak,
561                                 CIMD_Param_Nak_Cmd,(CIMD_Cmd+(unsigned)CIMD_Cmd_Ack)->code,     /* ==0x00 */
562                                 CIMD_Param_Nak_Error,CIMD_NAK_KEEPALIVE_REPLY,                  /* ==0x9998 */
563                                 CIMD_Param_Nak_NULL);
564                 alives--;
565                 }
566 }
567
568 /* RETURNS: Success
569  */
570 static bool CIMD_TX_SendCommandResend(boid)
571 {
572 int writephone_got;
573
574         writephone_got=WRITEPHONE(PortFD, CIMD_TX_SendCommand_buf, CIMD_TX_SendCommand_buf_d-CIMD_TX_SendCommand_buf);
575
576 #ifdef CIMD_DEBUG
577         write(1,"CMD:<STX>",9);
578         write(1,CIMD_TX_SendCommand_buf+1,CIMD_TX_SendCommand_buf_d-1-(CIMD_TX_SendCommand_buf+1));
579         write(1,"<ETX>\n",6);
580 #endif
581
582         return(writephone_got==(CIMD_TX_SendCommand_buf_d-CIMD_TX_SendCommand_buf));
583 }
584
585 static void CIMD_TX_SendCommand(GSM_Error *errorcodep,const struct CIMD_RX_Patrol *patrol,enum CIMD_Cmd_Type cmd,...)
586 {
587 va_list ap;
588 const struct CIMD_Cmd *cmdstruct;
589 enum CIMD_Param_SAMPLE param;
590 const struct CIMD_Param *paramstruct;
591 u8 *u8s,xsum;
592 struct CIMD_paramslot *slot;
593 int parami;
594
595         CIMD_KeepAlivesCheck();
596
597         va_start(ap,cmd);
598         if (errorcodep)
599                 *errorcodep=GE_BUSY;
600         CIMD_RX_Patrol_Current=patrol;
601
602         CIMD_TX_SendCommand_Cmd=cmd;
603         CIMD_TX_SendCommand_paramslots_full=0;
604         if ((unsigned)cmd>=CIMD_Cmdcnt)
605                 goto fail;              /* assert */
606         cmdstruct=CIMD_Cmd+(unsigned)cmd;
607
608         if (!CIMD_TX_SendCommand_pushparam(0/*code*/,CIMD_ELF_HexByte/*elf*/,cmdstruct->code))
609                 goto fail;
610         for (;;) {
611                 param=va_arg(ap,enum CIMD_Param_SAMPLE);
612                 if ((unsigned)param==cmdstruct->paramcnt)
613                         break;          /* CIMD_Param_*_NULL reached */
614                 if ((unsigned)param>cmdstruct->paramcnt)
615                         goto fail;      /* assert */
616                 paramstruct=(cmdstruct->param+(unsigned)param);
617                 if (!CIMD_TX_SendCommand_vpushparam(paramstruct->code,paramstruct->elf,&ap))
618                         goto fail;
619                 }
620         qsort(CIMD_TX_SendCommand_paramslots,CIMD_TX_SendCommand_paramslots_full,
621                         sizeof(*CIMD_TX_SendCommand_paramslots),
622                         (int (*)(const void *,const void *))CIMD_TX_SendCommand_paramslots_compare);
623
624         CIMD_TX_SendCommand_buf_d=CIMD_TX_SendCommand_buf;
625         *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_START;
626
627         slot=CIMD_TX_SendCommand_paramslots;
628         for (parami=0;slot<CIMD_TX_SendCommand_paramslots+CIMD_TX_SendCommand_paramslots_full;parami++) {
629                 if (CIMD_TX_SendCommand_buf_d +32/*safety*/ > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf)) {
630                                 /* error - overflow */
631 fail:
632                         if (errorcodep)
633                                 *errorcodep=GE_INTERNALERROR;
634                         return;
635                         }
636                 if (slot->code==parami) {
637                         if (!CIMD_TX_SendCommand_storeparam(slot))      /* error */
638                                 goto fail;
639                         }
640                 if (slot->code>=parami)
641                         *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_SEP;
642                 if (slot->code<=parami)
643                         slot++;
644                 }
645         xsum=0;
646         for (u8s=CIMD_TX_SendCommand_buf;u8s<CIMD_TX_SendCommand_buf_d;u8s++)
647                 xsum+=*u8s;
648         CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%02X",(unsigned)xsum);
649         *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_STOP;
650
651         CIMD_CatchBufferStart(errorcodep);
652
653         if (!CIMD_TX_SendCommandResend())
654                 goto fail;
655
656         /* success but we don't wait for the result code */
657 }
658
659 /* This function is used to get storage status from the phone. It currently
660    supports two different memory areas - internal and SIM. */
661
662 static GSM_Error 
663 wait_on(volatile GSM_Error *what, int timeout)
664 {
665 GSM_Error r=GE_TIMEOUT; /* shut up GCC when (timeout==0) */
666
667         while (timeout && ((r=*what)==GE_BUSY)) {
668                 if (!--timeout) {
669                         r=GE_TIMEOUT;
670                         break;
671                         }
672                 CIMD_KeepAlivesCheck();
673                 usleep(100000);
674         }
675         /* any specific patrollers are no longer valid */
676         CIMD_RX_Patrol_Current=NULL;
677
678 #ifdef CIMD_DEBUG
679         printf("wait_on finish, timeout=%d\n",(r==GE_TIMEOUT));
680 #endif
681
682         return(r);
683 }
684
685 #define WAIT_ON(what, timeout) \
686         do { \
687                 GSM_Error res = wait_on(what, timeout); \
688                 if (res != GE_NONE) \
689                         return res; \
690         } while (0)
691
692 /* I hope GCC gets this bunch optimized on the normal the normal errorcodep!=NULL case
693  */
694 #define CIMD_TX_SENDCOMMAND_WAIT_ON(errorcodep,timeout,patrol,args...) \
695         do { \
696 GSM_Error _CIMD_TX_SENDCOMMAND_WAIT_ON_err,*_CIMD_TX_SENDCOMMAND_WAIT_ON_errp; \
697  \
698                 if (!(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp=(errorcodep))) \
699                         _CIMD_TX_SENDCOMMAND_WAIT_ON_errp=&_CIMD_TX_SENDCOMMAND_WAIT_ON_err; \
700                 CIMD_TX_SendCommand(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp,(patrol),args); \
701                 WAIT_ON(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp,(timeout)); \
702         } while (0)
703
704
705 #define CIMD_ERR_WRAPPER(expr) \
706         do { \
707                 GSM_Error err=(expr); \
708                 if (err!=GE_NONE) \
709                         return(err); \
710         } while (0)
711
712 /* Convert SPEC field of BIP protocol using CIMD protocol on BMG<->SMSC link
713  */
714 static unsigned char CIMD_SMStoSPEC_BIP_CIMD(GSM_SMSMessage *SMS)
715 {
716         return(0
717                         /* bit 0 (enable) & bits 4-7 (value) - priority - not supported by Gnokii
718                          */
719                         |((!!SMS->ReplyViaSameSMSC)<<1) /*ReplyPath*/
720                         |((!!SMS->UDHPresent)<<2) /*UDH set*/
721                         );
722 }
723
724 static unsigned char CIMD_SMStoDCS(GSM_SMSMessage *SMS)
725 {
726 int class=(SMS->Class==-1 ? DEFAULT_CLASS : SMS->Class);
727
728 #if 0 /* NEVER send 0 for BIP CIMD as it would translate it to F6 !!!
729        */
730         if (!SMS->EightBit && class==DEFAULT_CLASS)
731                 return(0x00);
732 #endif
733         return(0xF0 | ((!!SMS->EightBit)<<2) | ((class&0x03)<<0));
734 }
735
736 static GSM_Error CIMD_PhoneSetup(void)
737 {
738 #if 1 /* HACK */
739         CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,100/*timeout*/,NULL/*patrol*/,
740                 CIMD_Cmd_Login,
741                         /* NULLs will be interpreted as ""
742                         */
743                         CIMD_Param_Login_ID,CFG_Get(CFG_Info,"CIMD","name"),
744                         CIMD_Param_Login_PWD,CFG_Get(CFG_Info,"CIMD","password"),
745                         CIMD_Param_Login_NULL);
746         if (!CIMD_RX_Patrol_Ack_Error)
747                 Revision[0]='\0';
748         else
749                 SAFE_STRNCPY_SIZEOF(Revision,CIMD_RX_Patrol_Ack_Error);
750
751         CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
752                 CIMD_Cmd_DCSEnable,
753                         CIMD_Param_DCSEnable_Type,(u16)CIMD_Param_DCSEnable_Type_PID_DCS_SPEC,
754                         CIMD_Param_DCSEnable_NULL);
755 #endif
756
757         return(GE_NONE);
758 }
759
760
761 static void CIMD_RX_Char(char rx_byte);
762 static void CIMD_SigHandler(int status);
763 static bool CIMD_OpenSerial(GSM_ConnectionType connection);
764
765 GSM_Phone phone_cimd;   /* forward declaration */
766
767 /* Initialise variables and state machine. */
768
769 static GSM_Error CIMD_Init(GSM_Data *data, GSM_Statemachine *state)
770 {
771         RequestTerminate = false;
772
773         SAFE_STRNCPY_SIZEOF(Model,data->Model);
774
775         /* Create and start main thread. */
776
777 #ifdef WIN32
778 {
779 int rtn;
780         rtn = ! OpenConnection(State->Link.PortDevice,CIMD_RX_Char,CIMD_KeepAliveProc);
781         if (rtn != 0) {
782                 return(GE_INTERNALERROR);
783 }
784 #else
785         SAFE_STRNCPY_SIZEOF(PortDevice,state->Link.PortDevice);
786         if (!CIMD_OpenSerial(state->Link.ConnectionType))
787                 return(GE_INTERNALERROR);
788 #endif
789
790         CIMD_ERR_WRAPPER(CIMD_PhoneSetup());
791
792         return (GE_NONE);
793 }
794
795 #if __unices__
796 /* thread for handling incoming data */
797 void CIMD_SelectLoop()
798 {
799         int err;
800         fd_set readfds;
801         struct timeval timeout;
802
803         FD_ZERO(&readfds);
804         FD_SET(device_portfd, &readfds);
805         /* set timeout to 15 seconds */
806         timeout.tv_sec=15;
807         timeout.tv_usec=0;
808         while (!RequestTerminate) {
809                 err = select(device_portfd + 1, &readfds, NULL, NULL, &timeout);
810                 /* call singal handler to process incoming data */
811                 if ( err > 0 ) CIMD_SigHandler(0);
812                 else if (err == -1) perror("Error in SelectLoop");
813         }
814 }
815 #endif
816
817 /* Applications should call CIMD_Terminate to shut down the CIMD thread and
818    close the serial port. */
819
820 static GSM_Error CIMD_Terminate(GSM_Data *data, GSM_Statemachine *state)
821 {
822 GSM_Error err;
823
824 #if 1 /* HACK */
825         /* Don't wait too much as we can have already broken link
826          */
827         CIMD_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
828                 CIMD_Cmd_Logout,
829                         CIMD_Param_Logout_NULL);
830         wait_on(&err,20/*timeout*/);            /* errors ignored, of course */
831 #endif
832
833         /* Request termination of thread */
834         RequestTerminate = true;
835
836 #ifndef WIN32
837         /* Now wait for thread to terminate. */
838         pthread_join(Thread, NULL);
839
840         /* Close serial port. */
841         device_close();
842
843 #else
844         CloseConnection();
845 #endif
846
847         return(GE_NONE);
848 }
849
850 /* messagecenter->No" is NOT set as it is input argument */
851 static void CIMD_MessageCenterClear(GSM_MessageCenter *messagecenter)
852 {
853         messagecenter->Name[0]='\0';            /* not present up to Nokia 9110i */
854         messagecenter->Recipient[0]='\0';       /* not present up to Nokia 9110i */
855         messagecenter->Number[0]='\0';          /* default */
856         messagecenter->Format=GSMF_Text;        /* default */
857         messagecenter->Validity=GSMV_72_Hours;  /* default */
858 }
859
860 static bool CIMD_RX_Patrol_CountReply(void)
861 {
862         CurrentSMSStatus->UnRead=/*FALLTHRU*/
863         CurrentSMSStatus->Used  =/*FALLTHRU*/
864         CurrentSMSStatus->Slots =/*FALLTHRU*/
865                         CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_CountReply_Used)->u.Decimal.i;
866
867         CurrentSMSStatusError=GE_NONE;
868         return(true);
869 }
870
871 static const struct CIMD_RX_Patrol CIMD_RX_Patrol_CountReply_struct=
872         { CIMD_Cmd_CountReply,CIMD_RX_Patrol_CountReply };
873
874 static GSM_Error CIMD_GetSMSStatus(GSM_Data *data, GSM_Statemachine *state)
875 {
876         CurrentSMSStatus = data->SMSStatus;
877         CurrentSMSStatus->UnRead=0;     /* default */
878         CurrentSMSStatus->Used  =0;     /* default */
879         CurrentSMSStatus->Slots =0;     /* default */
880 #if 1 /* HACK */
881         CIMD_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSStatusError/*errorcodep*/,100/*timeout*/,&CIMD_RX_Patrol_CountReply_struct/*patrol*/,
882                 CIMD_Cmd_Count,
883                         CIMD_Param_Count_NULL);
884 #endif
885
886         return(GE_NONE);
887 }
888
889 static GSM_Error CIMD_GetImei(GSM_Data *data, GSM_Statemachine *state)
890 {
891         /* not supported by the protocol */
892         data->Imei[0]='\0';
893         return (GE_NONE);
894 }
895
896 static GSM_Error CIMD_GetRevision(GSM_Data *data, GSM_Statemachine *state)
897 {
898         if (*Revision) {
899                 strcpy(data->Revision,Revision);
900                 return (GE_NONE);
901         } else return (GE_TRYAGAIN);
902 }
903
904 static GSM_Error CIMD_GetModel(GSM_Data *data, GSM_Statemachine *state)
905 {
906         /* not supported by the protocol */
907         strcpy(data->Model,Model);
908         return (GE_NONE);
909 }
910
911 static GSM_Error CIMD_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
912 {
913         /* not supported by the protocol */
914         data->Imei[0]='\0';
915         return (GE_NONE);
916 }
917
918 static void CIMD_DateTimeSetCurrent(GSM_DateTime *datetime)
919 {
920 time_t current=time(NULL);
921 struct tm *tm=localtime(&current);
922
923         datetime->AlarmEnabled=false;
924
925         datetime->Year  =tm->tm_year+1900;
926         datetime->Month =tm->tm_mon+1;
927         datetime->Day   =tm->tm_mday;
928         datetime->Hour  =tm->tm_hour;
929         datetime->Minute=tm->tm_min;
930         datetime->Second=tm->tm_sec;
931
932         datetime->Timezone=timezone;
933 }
934
935 /* scts=="18.06.1998 22:33:23"
936  */
937 static bool CIMD_SCTStoSMS(GSM_SMSMessage *SMS,const char *scts)
938 {
939 GSM_DateTime *DateTime=&SMS->Time;
940 const char *fmt="%2d.%2d.%4d %2d:%2d:%2d";      /* trailing '\0' IS used! */
941 const char *fs,*ss;
942
943         fs=fmt;
944         ss=scts;
945         do {
946                 while (*fs=='%') {
947 int nums=(*++fs)-'0';
948                         while (nums--)
949                                 if (!isdigit(*ss++))
950                                         return(false);
951                         }
952                 if (*fs++!=*ss)
953                         return(false);
954                 } while (fs[-1]);
955         /* string is completely valid now */
956
957         sscanf(scts,fmt,
958                         &DateTime->Day,
959                         &DateTime->Month,
960                         &DateTime->Year,
961                         &DateTime->Hour,
962                         &DateTime->Minute,
963                         &DateTime->Second);
964         DateTime->Timezone=0;
965
966         return(true);
967 }
968
969 static void CIMD_DCStoSMS(GSM_SMSMessage *SMS,unsigned char dcs)
970 {
971         switch ((dcs&0xF0)>>4) {
972                 case 0x0:
973                         switch (dcs&0x0F) {
974                                 case 0x0:
975                                         CurrentSMSMessage->EightBit=false;
976                                         break;
977                                 }
978                         break;
979                 case 0xF:
980                         CurrentSMSMessage->EightBit=!!(dcs&0x04);       /* bit 2 */
981                         CurrentSMSMessage->Class=(dcs&0x03);            /* bits 0 & 1 */
982                         break;
983                 }
984 }
985
986 /* Convert SPEC field of BIP protocol using CIMD protocol on BMG<->SMSC link
987  */
988 static void CIMD_SPEC_BIP_CIMDtoSMS(GSM_SMSMessage *SMS,u8 spec)
989 {
990         /* Such specification not supported by BIP:
991          */
992         CurrentSMSMessage->Type=GST_MT;
993         CurrentSMSMessage->Status=GSS_NOTSENTREAD;
994
995         /* bit 0 (enable) & bits 4-7 (value) - priority - not supported by Gnokii
996          */
997
998         SMS->UDHPresent=!!(spec&(1<<2));
999
1000         SMS->ReplyViaSameSMSC=!!(spec&(1<<1));
1001 }
1002
1003 static bool CIMD_RX_Patrol_RetrieveReply(void)
1004 {
1005 u16 pid_dcs;    /* upper=PID, lower=DCS */
1006
1007         {
1008 const char *destination=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Destination)->u.StringZ.s;
1009
1010                 if (strlen(destination)+1 > sizeof(CurrentSMSMessage->Sender)) {
1011 fail:
1012                         CurrentSMSMessageError=GE_INTERNALERROR;
1013                         return(true);           /* error */
1014                         }
1015                 strcpy(CurrentSMSMessage->Sender,destination);
1016                 }
1017
1018         if ((CurrentSMSMessage->EightBit=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Is8bit)->u.Decimal.i)) {
1019 const char *hextext=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Text)->u.StringZ.s;
1020 size_t hextextlen=strlen(hextext);
1021
1022                 if ((hextextlen&1) || (hextextlen/2 > sizeof(CurrentSMSMessage->MessageText)))
1023                         goto fail;              /* error - message too long */
1024                 if (!SMS_BlockFromHex(CurrentSMSMessage->MessageText/*d*/,hextext,hextextlen))
1025                         goto fail;              /* error - parse error */
1026                 CurrentSMSMessage->MessageTextLength=hextextlen/2;
1027                 }
1028         else {  /* 7bit */
1029 const char *text=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Text)->u.StringZ.s;
1030 size_t textlen=strlen(text);
1031
1032                 if (textlen+1 > sizeof(CurrentSMSMessage->MessageText))
1033                         goto fail;              /* error - message too long */
1034                 strcpy(CurrentSMSMessage->MessageText,text);
1035                 CurrentSMSMessage->MessageTextLength=textlen;
1036                 }
1037
1038         /* errors ignored as it is not fatal */
1039         CIMD_SCTStoSMS(CurrentSMSMessage,CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Timestamp)->u.StringZ.s);
1040
1041         pid_dcs=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_PID_DCS)->u.HexWord.i;
1042
1043         /* FIXME: Is it correct to fill "MessageCenter" fields from SMS body? */
1044         CurrentSMSMessage->MessageCenter.Format=(GSM_SMSMessageFormat)(pid_dcs>>8U);    /* <pid> */
1045         CIMD_DCStoSMS(CurrentSMSMessage,(pid_dcs&0x00FFU));
1046         CIMD_SPEC_BIP_CIMDtoSMS(CurrentSMSMessage,CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_SPEC)->u.HexByte.i);
1047
1048         if (CurrentSMSMessage->UDHPresent) {
1049 u8 udhlen;
1050                 if (CurrentSMSMessage->MessageTextLength<=0)
1051                         goto fail;
1052                 udhlen=1/*sizeof(udhlen)*/ +CurrentSMSMessage->MessageText[0];
1053                 if (udhlen > CurrentSMSMessage->MessageTextLength)
1054                         goto fail;
1055                 memcpy(CurrentSMSMessage->UDH,CurrentSMSMessage->MessageText,udhlen);
1056                 memmove(CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageText+udhlen,
1057                                 CurrentSMSMessage->MessageTextLength-udhlen +1/*Trailing '\0'*/);
1058                 CurrentSMSMessage->MessageTextLength-=udhlen;
1059                 }
1060
1061         CurrentSMSMessageError=GE_NONE;
1062         return(true);
1063 }
1064
1065 static const struct CIMD_RX_Patrol CIMD_RX_Patrol_RetrieveReply_struct=
1066         { CIMD_Cmd_RetrieveReply,CIMD_RX_Patrol_RetrieveReply };
1067
1068 static GSM_Error CIMD_GetSMS(GSM_Data *data, GSM_Statemachine *state)
1069 {
1070         CurrentSMSMessage = data->SMSMessage;
1071
1072         if (CurrentSMSMessage->MemoryType!=GMT_SM)
1073                 return(GE_INVALIDMEMORYTYPE);
1074
1075         CIMD_DateTimeSetCurrent(&CurrentSMSMessage->Time);              /* default */
1076         CIMD_DateTimeSetCurrent(&CurrentSMSMessage->SMSCTime);          /* not present in the protocol */
1077         CurrentSMSMessage->MessageTextLength=0;                         /* default */
1078         CurrentSMSMessage->Validity=72/*hours*/*60;                     /* default */
1079         CurrentSMSMessage->UDHPresent=false;                            /* default */
1080         CurrentSMSMessage->MessageText[0]='\0';                         /* default */
1081
1082         CIMD_MessageCenterClear(&CurrentSMSMessage->MessageCenter);     /* default */
1083         CurrentSMSMessage->MessageCenter.No=0;                          /* default - input for GetSMSCenter */
1084
1085         CurrentSMSMessage->Sender[0]='\0';                              /* default */
1086         CurrentSMSMessage->Destination[0]='\0';                         /* default */
1087         CurrentSMSMessage->MessageNumber=CurrentSMSMessage->Location;   /* default */
1088         /* CurrentSMSMessage->MemoryType is input argument */
1089         CurrentSMSMessage->Type=GST_UN;                                 /* default, detection of EMPTY SMSes! */
1090         CurrentSMSMessage->Status=GSS_SENTREAD;                         /* default */
1091         CurrentSMSMessage->Class=DEFAULT_CLASS;                         /* default */
1092         CurrentSMSMessage->EightBit=false;                              /* default */
1093         CurrentSMSMessage->Compression=false;                           /* default */
1094         /* CurrentSMSMessage->Location is input argument */
1095         CurrentSMSMessage->ReplyViaSameSMSC=false;                      /* default */
1096
1097         CIMD_TX_SendCommand(&CurrentSMSMessageError/*errorcodep*/,&CIMD_RX_Patrol_RetrieveReply_struct/*patrol*/,
1098                 CIMD_Cmd_Retrieve,
1099                         CIMD_Param_Retrieve_NULL);
1100         if (GE_NONE!=wait_on(&CurrentSMSMessageError,90/*timeout*/)) {
1101                 if (CIMD_RX_Patrol_Nak_Error!=CIMD_NAK_NO_SMS)
1102                         return(CurrentSMSMessageError);
1103                 /* We don't return GE_EMPTYSMSLOCATION as when we have already eaten
1104                  * all the wating SMSes there cannot be any other one
1105                  */
1106                 return(GE_INVALIDSMSLOCATION);
1107                 }
1108
1109         return(GE_NONE);
1110 }
1111
1112 static GSM_Error CIMD_SendSMS(GSM_Data *data, GSM_Statemachine *state)
1113 {
1114 u8 bintext[sizeof(CurrentSMSMessage->UDH)+sizeof(CurrentSMSMessage->MessageText)],*d;
1115 char hexbintext[sizeof(bintext) +1/*Trailing '\0'*/];
1116
1117         CurrentSMSMessage = data->SMSMessage;
1118
1119         CurrentSMSMessage->MessageNumber=0;     /* default */
1120
1121         d=bintext;
1122         if (CurrentSMSMessage->UDHPresent) {
1123 size_t UDHlen=1+CurrentSMSMessage->UDH[0];
1124
1125                 memcpy(d,CurrentSMSMessage->UDH,UDHlen);
1126                 d+=UDHlen;
1127 #ifdef CIMD_SUBMIT_7BIT
1128                 if (!CurrentSMSMessage->EightBit)       /* We are not able to send UDH by 7bit channel */
1129                         return(GE_NOTSUPPORTED);
1130 #endif
1131                 }
1132         if (
1133 #ifdef CIMD_SUBMIT_7BIT
1134                         1       /* Never do 7bit->8bit encoding when CIMD_SUBMIT_7BIT */
1135 #else
1136                         CurrentSMSMessage->EightBit
1137 #endif
1138                         ) {
1139                 memcpy(d,CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageTextLength);
1140                 d+=CurrentSMSMessage->MessageTextLength;
1141         }
1142 #ifndef CIMD_SUBMIT_7BIT
1143         else {
1144 size_t byteslen=PackSevenBitsToEight(
1145                         /* check it out yourself, really the number of used bits for UDH header on the start
1146                          * as we will need to allocate initial bit to align SMS->MessageText on the 7-bit boundary
1147                          */
1148                         (7-(d-bintext))%7,
1149                         CurrentSMSMessage->MessageText,d);
1150                 d+=byteslen;
1151         }
1152 #endif
1153
1154         if (
1155 #ifdef CIMD_SUBMIT_7BIT
1156                         CurrentSMSMessage->EightBit
1157 #else
1158                         1       /* Never use plain Submit for 8bit messages */
1159 #endif
1160                         ) {
1161                 *(SMS_BlockToHex(hexbintext,bintext,(d-bintext)/*len*/))='\0';
1162
1163                 /* DANGER: Keep in sync with CIMD_Cmd_Submit !!!
1164                  */
1165                 CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,150/*timeout*/,NULL/*patrol*/,
1166                         CIMD_Cmd_SubmitBin,
1167                                 CIMD_Param_SubmitBin_Destination,CurrentSMSMessage->Destination,
1168                                 CIMD_Param_SubmitBin_Text,hexbintext,
1169                                 CIMD_Param_SubmitBin_ValidityPeriod,SMS_Validity_to_VP(CurrentSMSMessage->Validity),
1170                                 CIMD_Param_SubmitBin_AUX,               /* Empty */
1171                                 CIMD_Param_SubmitBin_DCS,(unsigned)CIMD_SMStoDCS(CurrentSMSMessage),
1172                                 CIMD_Param_SubmitBin_PID,(u8)CurrentSMSMessage->MessageCenter.Format,
1173                                 CIMD_Param_SubmitBin_SPEC,CIMD_SMStoSPEC_BIP_CIMD(CurrentSMSMessage),
1174                                 CIMD_Param_SubmitBin_NULL);
1175                 }
1176 #ifdef CIMD_SUBMIT_7BIT
1177         else {
1178                 *d='\0';
1179
1180                 /* DANGER: Keep in sync with CIMD_Cmd_SubmitBin !!!
1181                  */
1182                 CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,150/*timeout*/,NULL/*patrol*/,
1183                         CIMD_Cmd_Submit,
1184                                 CIMD_Param_Submit_Destination,CurrentSMSMessage->Destination,
1185                                 CIMD_Param_Submit_Text,bintext,
1186                                 CIMD_Param_Submit_ValidityPeriod,SMS_Validity_to_VP(CurrentSMSMessage->Validity),
1187                                 CIMD_Param_Submit_AUX,          /* Empty */
1188                                 CIMD_Param_Submit_DCS,(unsigned)CIMD_SMStoDCS(CurrentSMSMessage),
1189                                 CIMD_Param_Submit_PID,(u8)CurrentSMSMessage->MessageCenter.Format,
1190                                 CIMD_Param_Submit_SPEC,CIMD_SMStoSPEC_BIP_CIMD(CurrentSMSMessage),
1191                                 CIMD_Param_Submit_NULL);
1192                 }
1193 #endif
1194
1195         return(GE_SMSSENDOK);
1196 }
1197
1198 static GSM_Error CIMD_Reset(GSM_Data *data, GSM_Statemachine *state)
1199 {
1200         CIMD_ERR_WRAPPER(CIMD_PhoneSetup());
1201
1202         return(GE_NONE);
1203 }
1204
1205 #ifndef WIN32
1206
1207 /* Called by initialisation code to open comm port in asynchronous mode. */
1208
1209 static bool CIMD_OpenSerial(GSM_ConnectionType connection)
1210 {
1211         int result;
1212   
1213 #if __unices__
1214         int rtn;
1215 #else
1216         struct sigaction sig_io;
1217
1218         /* Set up and install handler before enabling async IO on port. */
1219
1220         sig_io.sa_handler = CIMD_SigHandler;
1221         sig_io.sa_flags = 0;
1222         sigaction (SIGIO, &sig_io, NULL);
1223 #endif
1224
1225         /* Open device. */
1226
1227         result = device_open(PortDevice, false/*with_odd_parity*/, true/*with_async*/, -1/*with_hw_handshake*/, connection);
1228
1229         if (!result) {
1230                 perror(_("Couldn't open CIMD device"));
1231                 return false;
1232         }
1233
1234 #if __unices__
1235         /* create a thread to handle incoming data from mobile phone */
1236         rtn = pthread_create(&selThread, NULL, (void*)CIMD_SelectLoop, (void*)NULL);
1237         if (rtn != 0) return false;
1238 #endif
1239
1240         /* device_changespeed() not needed as device_open() now automatically
1241          * sets the user-specified (or default) speed.
1242          */
1243         return (true);
1244 }
1245
1246 static void CIMD_SigHandler(int status)
1247 {
1248         unsigned char buffer[255];
1249         int count, res;
1250         res = device_read(buffer, 255);
1251         for (count = 0; count < res ; count ++)
1252                 CIMD_RX_Char(buffer[count]);
1253 }
1254 #endif /* WIN32 */
1255
1256 static bool CIMD_RX_Patrol_Ack(void)
1257 {
1258         if ((CIMD_Cmd+(unsigned)CIMD_Cmd_Nak)->code == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Cmd)->u.HexByte.i) { /* ==0x99 */
1259                 /* We cannot send the keepalive here as some dangerous operation may be in progress
1260                  */
1261                 CIMD_KeepAlivesLockInit();
1262                 pthread_mutex_lock(&CIMD_KeepAlivesLock);
1263                 CIMD_KeepAlives++;
1264                 pthread_mutex_unlock(&CIMD_KeepAlivesLock);
1265                 return(false);
1266                 }
1267
1268         if (CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Cmd)->u.HexByte.i!=(CIMD_Cmd+(unsigned)CIMD_TX_SendCommand_Cmd)->code)
1269                 return(false);          /* Ack for some unknown command */
1270
1271         free(CIMD_RX_Patrol_Ack_Error);
1272         CIMD_RX_Patrol_Ack_Error=strdup(CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Error)->u.StringZ.s);
1273
1274         /* Some patrol may have already indicated some error!
1275          */
1276          if (CIMD_CatchBufferErrorP && *CIMD_CatchBufferErrorP==GE_BUSY)
1277                  *CIMD_CatchBufferErrorP=GE_NONE;
1278
1279         return(true);
1280 }
1281
1282 static void CIMD_RX_Patrol_Ack_reset(void)
1283 {
1284         free(CIMD_RX_Patrol_Ack_Error);
1285         CIMD_RX_Patrol_Ack_Error=NULL;
1286 }
1287
1288 static const char *CIMD_RX_Patrol_Nak_resolve(u16 errorcode)
1289 {
1290 const struct CIMD_Param_Ack_Nak_Error *errorp;
1291
1292         for (errorp=CIMD_Param_Ack_Nak_Error;errorp<CIMD_Param_Ack_Nak_Error+ARRAY_LEN(CIMD_Param_Ack_Nak_Error);errorp++)
1293                 if (errorcode==errorp->code)
1294                         return(_(errorp->msg));
1295         return(_("Unknown error code"));
1296 }
1297
1298 static void CIMD_RX_Patrol_Nak_dump(u16 errorcode)
1299 {
1300         fprintf(stderr,_("Got CIMD error code 0x%04X: "),errorcode);
1301         if (!(errorcode & ~0x000F)) {   /* combined error code */
1302 u16 errormask;
1303                 fprintf(stderr,_("combined:"));
1304                 for (errormask=0x0001;errormask<=errorcode;errormask<<=1)
1305                         if (errorcode&errormask)
1306                                 fprintf(stderr," +%s",CIMD_RX_Patrol_Nak_resolve(errormask));
1307                 fputc('\n',stderr);
1308                 return;
1309                 }
1310         fprintf(stderr,"%s\n",CIMD_RX_Patrol_Nak_resolve(errorcode));
1311 }
1312
1313 static bool CIMD_RX_Patrol_Nak(void)
1314 {
1315         if ((CIMD_Cmd+(unsigned)CIMD_Cmd_Nak)->code == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Cmd)->u.HexByte.i        /* ==0x99 */
1316                 && CIMD_NAK_RESEND == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Error)->u.HexWord.i) {    /* 0x9999 */
1317                 fprintf(stderr,_("WARNING: Requested to resend last packet!!\n"));
1318                 CIMD_TX_SendCommandResend();
1319                 return(true);
1320                 }
1321
1322         if (CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Cmd)->u.HexByte.i!=(CIMD_Cmd+(unsigned)CIMD_TX_SendCommand_Cmd)->code)
1323                 return(false);          /* Nak for some unknown command */
1324
1325         CIMD_RX_Patrol_Nak_Error=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Error)->u.HexWord.i;
1326         if (CIMD_CatchBufferErrorP)
1327                 *CIMD_CatchBufferErrorP=GE_INTERNALERROR;
1328
1329         CIMD_RX_Patrol_Nak_dump(CIMD_RX_Patrol_Nak_Error);
1330
1331         return(true);
1332 }
1333
1334 static void CIMD_RX_Patrol_Nak_reset(void)
1335 {
1336         CIMD_RX_Patrol_Nak_Error=0;
1337 }
1338
1339 static const struct CIMD_RX_Patrol CIMD_RX_Patrols[]={
1340         { CIMD_Cmd_Ack,CIMD_RX_Patrol_Ack,CIMD_RX_Patrol_Ack_reset },
1341         { CIMD_Cmd_Nak,CIMD_RX_Patrol_Nak,CIMD_RX_Patrol_Nak_reset },
1342         };
1343
1344
1345 static void CIMD_CatchBufferReset(void)
1346 {
1347         CIMD_CatchBufferPtr=CIMD_CatchBuffer;
1348 }
1349
1350 static void CIMD_CatchBufferStart(GSM_Error *errorcodep)
1351 {
1352 static GSM_Error err_trashcan;
1353 const struct CIMD_RX_Patrol *patrol;
1354
1355         if (!errorcodep)
1356                 errorcodep=&err_trashcan;
1357         CIMD_CatchBufferErrorP=errorcodep;
1358
1359         CIMD_CatchBufferReset();
1360
1361         for (patrol=CIMD_RX_Patrols;patrol<CIMD_RX_Patrols+ARRAY_LEN(CIMD_RX_Patrols);patrol++)
1362                 if (patrol->reset)
1363                         (*patrol->reset)();
1364 }
1365
1366 static u8 *CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1367
1368 /* RETURNS: Success
1369  */
1370 static bool CIMD_RX_ProcessRawPacket_parseparam(struct CIMD_paramslot *slot)
1371 {
1372         switch (slot->elf) {
1373
1374                 case CIMD_ELF_HexByte: {
1375 unsigned parsedbyte;
1376                         if (0   || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[0])
1377                                 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[1])
1378                                 || CIMD_MARK_SEP!=CIMD_RX_ProcessRawPacket_parseparam_buf_s[2]
1379                                 )
1380                                 return(false);
1381                         if (1!=sscanf(CIMD_RX_ProcessRawPacket_parseparam_buf_s,"%X" CIMD_MARK_SEPS,&parsedbyte))
1382                                 return(false);
1383                         slot->u.HexByte.i=parsedbyte;
1384                         CIMD_RX_ProcessRawPacket_parseparam_buf_s+=2+1;
1385                         } break;
1386
1387                 case CIMD_ELF_HexWord: {
1388 unsigned parsedword;
1389                         if (0   || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[0])
1390                                 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[1])
1391                                 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[2])
1392                                 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[3])
1393                                 || CIMD_MARK_SEP!=CIMD_RX_ProcessRawPacket_parseparam_buf_s[4]
1394                                 )
1395                                 return(false);
1396                         if (1!=sscanf(CIMD_RX_ProcessRawPacket_parseparam_buf_s,"%X" CIMD_MARK_SEPS,&parsedword))
1397                                 return(false);
1398                         slot->u.HexWord.i=parsedword;
1399                         CIMD_RX_ProcessRawPacket_parseparam_buf_s+=4+1;
1400                         } break;
1401
1402                 case CIMD_ELF_Decimal: {
1403 int parsedint;
1404 u8 *start=CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1405                         if (CIMD_MARK_SEP==*start)
1406                                 return(false);          /* empty Decimal not permitted */
1407                         while (isdigit(*CIMD_RX_ProcessRawPacket_parseparam_buf_s))
1408                                 CIMD_RX_ProcessRawPacket_parseparam_buf_s++;
1409                         if (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s++)        /* skip CIMD_MARK_SEP */
1410                                 return(false);
1411                         if (1!=sscanf(start,"%d" CIMD_MARK_SEPS,&parsedint))
1412                                 return(false);
1413                         slot->u.Decimal.i=parsedint;
1414                         } break;
1415
1416                 case CIMD_ELF_StringZ:
1417                         slot->u.StringZ.s=CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1418                         while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s)
1419                                 CIMD_RX_ProcessRawPacket_parseparam_buf_s++;
1420                         *CIMD_RX_ProcessRawPacket_parseparam_buf_s++='\0';      /* skip CIMD_MARK_SEP */
1421                         break;
1422
1423                 case CIMD_ELF_HexBlock:
1424                         slot->u.HexBlock.buf=CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1425
1426                         while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s)
1427                                 CIMD_RX_ProcessRawPacket_parseparam_buf_s++;
1428                         if ((CIMD_RX_ProcessRawPacket_parseparam_buf_s-slot->u.HexBlock.buf)&1)
1429                                 return(false);          /* odd number of xdigits */
1430
1431                         slot->u.HexBlock.len=(CIMD_RX_ProcessRawPacket_parseparam_buf_s-slot->u.HexBlock.buf);
1432                         if (!SMS_BlockFromHex(slot->u.HexBlock.buf/*d*/,slot->u.HexBlock.buf/*s*/,slot->u.HexBlock.len*2))
1433                                 return(false);          /* parse error */
1434
1435                         CIMD_RX_ProcessRawPacket_parseparam_buf_s++;    /* skip CIMD_MARK_SEP */
1436                         break;
1437
1438                 case CIMD_ELF_Empty:
1439                         if (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s)
1440                                 return(false);          /* some content found */
1441                         break;
1442                 }
1443         return(true);   /* success */
1444 }
1445
1446
1447 static bool CIMD_RX_EvalPatrol(const struct CIMD_RX_Patrol *patrol)
1448 {
1449         if (!patrol)
1450                 return(false);  /* not recognized */
1451         if (CIMD_RX_Packet_Cmd!=patrol->cmd)
1452                 return(false);  /* not recognized */
1453         return((*patrol->func)());
1454 }
1455
1456 static inline void CIMD_RX_ProcessPacket(void)
1457 {
1458 const struct CIMD_RX_Patrol *patrol;
1459
1460         for (patrol=CIMD_RX_Patrols;patrol<CIMD_RX_Patrols+ARRAY_LEN(CIMD_RX_Patrols);patrol++)
1461                 if (CIMD_RX_EvalPatrol(patrol))
1462                         return;         /* recognized and processed - whether successfuly is not interesting */
1463         CIMD_RX_EvalPatrol(CIMD_RX_Patrol_Current);
1464 }
1465
1466 static inline void CIMD_RX_ProcessRawPacket(void)
1467 {
1468 unsigned xsumgot;
1469 u8 *u8s,xsum;
1470 u16 position;
1471 const struct CIMD_Param *param;
1472 const struct CIMD_Cmd *cmdp;
1473
1474 #ifdef CIMD_DEBUG
1475         write(1,"GOT:<STX>",9);
1476         write(1,CIMD_CatchBuffer+1,CIMD_CatchBufferPtr-1-(CIMD_CatchBuffer+1));
1477         write(1,"<ETX>\n",6);
1478 #endif
1479
1480         if (CIMD_CatchBufferPtr<CIMD_CatchBuffer +1/*START*/ +1/*SEP*/ +2/*xsum*/ +1/*STOP*/)
1481                 return;         /* error - buffer too short */
1482         if (0   || CIMD_CatchBufferPtr[-4]!=CIMD_MARK_SEP
1483                 || !isxdigit(CIMD_CatchBufferPtr[-3])
1484                 || !isxdigit(CIMD_CatchBufferPtr[-2])
1485                 )
1486                 return;         /* error - invalid buffer tail */
1487
1488         /* (*Ptr) will now point to the end of parameter part */
1489         CIMD_CatchBufferPtr-=3;
1490         if (1!=sscanf(CIMD_CatchBufferPtr,"%X" CIMD_MARK_SEPS,&xsumgot))
1491                 return;         /* INTERNAL - we have already checked the validity! */
1492         xsum=0;
1493         for (u8s=CIMD_CatchBuffer;u8s<CIMD_CatchBufferPtr;u8s++)
1494                 xsum+=*u8s;
1495         if (xsum!=xsumgot)
1496                 return;         /* error - invalid checksum */
1497
1498         CIMD_RX_ProcessRawPacket_parseparam_buf_s=CIMD_CatchBuffer +1/*START*/;
1499         CIMD_RX_Packet_slots=1;         /* just [0] now */
1500         CIMD_RX_Packet_slot[0].code=0;  /* ignored now - position */
1501         CIMD_RX_Packet_slot[0].elf=CIMD_ELF_HexByte;
1502         if (!CIMD_RX_ProcessRawPacket_parseparam(CIMD_RX_Packet_slot+0))
1503                 return;         /* error - cmd code not parsed */
1504
1505         for (cmdp=CIMD_Cmd;cmdp<CIMD_Cmd+CIMD_Cmdcnt;cmdp++)
1506                 if (cmdp->code==CIMD_RX_Packet_slot[0].u.HexByte.i) break;
1507         if (cmdp>=CIMD_Cmd+CIMD_Cmdcnt)
1508                 return;         /* error - unknown command */
1509         CIMD_RX_Packet_Cmd=(enum CIMD_Cmd_Type)(cmdp-CIMD_Cmd);
1510
1511         /* "position" handling is BIP dependency! */
1512         for (position=1;CIMD_RX_ProcessRawPacket_parseparam_buf_s<CIMD_CatchBufferPtr;position++) {
1513 struct CIMD_paramslot *slot;
1514
1515                 /* This code search is a crude overhead just for BIP now :-)
1516                  */
1517                 for (param=cmdp->param;param<cmdp->param+cmdp->paramcnt;param++)
1518                         if (position==param->code)
1519                                 break;
1520                 if (param>=cmdp->param+cmdp->paramcnt) {        /* not found - ignore */
1521                         while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s++);    /* skip CIMD_MARK_SEP */
1522                         continue;
1523                         }
1524                 slot=CIMD_RX_Packet_slot+1+(param-cmdp->param);
1525                 slot->code=param->code;
1526                 slot->elf=param->elf;
1527                 if (!CIMD_RX_ProcessRawPacket_parseparam(slot))
1528                         return;         /* error - unable to parse required param */
1529                 /* CIMD_MARK_SEP is skipped by CIMD_RX_ProcessRawPacket_parseparam() automatically
1530                  */
1531                 CIMD_RX_Packet_slots++;
1532                 }
1533         /* The packet is now parsed */
1534
1535         CIMD_RX_ProcessPacket();
1536 }
1537
1538 /* RX_State machine for receive handling.  Called once for each character
1539    received from the phone/phone. */
1540
1541 static void CIMD_RX_Char(char rx_byte)
1542 {
1543 #ifdef CIMD_DEBUG
1544 #if 0
1545         dprintf(_("Received character '%c' (0x%02X)\n"),
1546                 (!isgraph(rx_byte) ? '?' : rx_byte),(unsigned char)rx_byte);
1547 #endif
1548         putchar(rx_byte);
1549 #endif
1550
1551         /* NEVER store '\0' as it would knock-out completely our patrolling system!
1552          * It is invalid anyway
1553          */
1554         if (rx_byte=='\0') {
1555                 CIMD_CatchBufferReset();
1556                 return;
1557                 }
1558
1559         if (CIMD_CatchBufferPtr==CIMD_CatchBuffer && rx_byte!=CIMD_MARK_START) {
1560                 /* No haven't yet catched any START yet */
1561                 return;
1562                 }
1563         if (CIMD_CatchBufferPtr>=CIMD_CatchBuffer+sizeof(CIMD_CatchBuffer)) {
1564                 fprintf(stderr,_("WARNING: Incoming CIMD packet too long to fit (>%d bytes)!\n"),sizeof(CIMD_CatchBuffer));
1565                 CIMD_CatchBufferReset();
1566                 }
1567
1568         *CIMD_CatchBufferPtr++=rx_byte;
1569         if (rx_byte==CIMD_MARK_STOP) {
1570                 CIMD_RX_ProcessRawPacket();
1571                 CIMD_CatchBufferReset();
1572                 }
1573 }
1574
1575 /* Here we initialise model specific functions. */
1576
1577 #define CIMD_FUNCTIONS_ENTRY(name) \
1578         case GOP_##name:        return(CIMD_##name(data,state));
1579
1580 static GSM_Error CIMD_Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
1581 {
1582 static GSM_Statemachine *catcher=NULL;
1583
1584         if (!catcher || catcher==state)
1585                 catcher=state;
1586         else
1587                 *((char *)NULL)=1;
1588
1589         switch (op) {
1590                 CIMD_FUNCTIONS_ENTRY(Init)
1591                 CIMD_FUNCTIONS_ENTRY(Terminate)
1592                 CIMD_FUNCTIONS_ENTRY(GetSMSStatus)
1593                 CIMD_FUNCTIONS_ENTRY(GetSMS)
1594                 CIMD_FUNCTIONS_ENTRY(SendSMS)
1595                 CIMD_FUNCTIONS_ENTRY(GetImei)
1596                 CIMD_FUNCTIONS_ENTRY(GetRevision)
1597                 CIMD_FUNCTIONS_ENTRY(GetModel)
1598                 CIMD_FUNCTIONS_ENTRY(GetManufacturer)
1599                 CIMD_FUNCTIONS_ENTRY(Reset)
1600
1601                 case GOP_Identify: {
1602 GSM_Error err,r=GE_NONE;
1603                         if (GE_NONE!=(err=CIMD_GetImei        (data,state)))
1604                                 r=err;
1605                         if (GE_NONE!=(err=CIMD_GetRevision    (data,state)))
1606                                 r=err;
1607                         if (GE_NONE!=(err=CIMD_GetModel       (data,state)))
1608                                 r=err;
1609                         if (GE_NONE!=(err=CIMD_GetManufacturer(data,state)))
1610                                 r=err;
1611                         return(r);
1612                         }
1613
1614                 default:
1615                         return(GE_NOTIMPLEMENTED);
1616         }
1617 }
1618
1619 GSM_Phone phone_cimd = {
1620         UNIMPLEMENTED,  /* IncomingFunctions - we don't use default StateMachine */
1621         UNIMPLEMENTED,  /* DefaultFunction   - we don't use default StateMachine */
1622         /* Mobile phone information */
1623         {
1624                 "BIP" /* |CIMD - not yet */,    /* Supported models */
1625                 100,                    /* Max RF Level (AT+CSQ) */
1626                 0,                      /* Min RF Level (AT+CSQ) */
1627                 GRF_Percentage,         /* RF level units */
1628                 100,                    /* Max Battery Level (AT+CBC) */
1629                 0,                      /* Min Battery Level (AT+CBC) */
1630                 GBU_Percentage,         /* Battery level units */
1631                 GDT_None,               /* Have date/time support */
1632                 GDT_None,               /* Alarm supports time only */
1633                 0,                      /* No alarm available */
1634                 48, 84,                 /* Startup logo size */
1635                 14, 72,                 /* Op logo size */
1636                 14, 72,                 /* Caller logo size */
1637         },
1638         CIMD_Functions,
1639 };