* Fixed a crashy crashy bug in the AGUP protocol function in the client
[citadel.git] / citadel / citadel_ipc.c
1 /* $Id$ */
2
3 #include "sysdep.h"
4 #if TIME_WITH_SYS_TIME
5 # include <sys/time.h>
6 # include <time.h>
7 #else
8 # if HAVE_SYS_TIME_H
9 #  include <sys/time.h>
10 # else
11 #  include <time.h>
12 # endif
13 #endif
14 #include <stdio.h>
15 #include <sys/types.h>
16 #include <string.h>
17 #include <malloc.h>
18 #include <stdlib.h>
19 #include <ctype.h>
20 #ifdef THREADED_CLIENT
21 #include <pthread.h>
22 #endif
23 #include "citadel.h"
24 #include "citadel_ipc.h"
25 #include "citadel_decls.h"
26 #include "client_crypto.h"
27 #include "tools.h"
28
29 #ifdef THREADED_CLIENT
30 pthread_mutex_t rwlock;
31 #endif
32 char express_msgs = 0;
33
34 static volatile int download_in_progress = 0;   /* download file open */
35 static volatile int upload_in_progress = 0;     /* upload file open */
36 /* static volatile int serv_sock; */    /* Socket on which we talk to server */
37
38
39 /*
40  * Does nothing.  The server should always return 200.
41  */
42 int CtdlIPCNoop(void)
43 {
44         char aaa[128];
45
46         return CtdlIPCGenericCommand("NOOP", NULL, 0, NULL, NULL, aaa);
47 }
48
49
50 /*
51  * Does nothing interesting.  The server should always return 200
52  * along with your string.
53  */
54 int CtdlIPCEcho(const char *arg, char *cret)
55 {
56         register int ret;
57         char *aaa;
58         
59         if (!arg) return -2;
60         if (!cret) return -2;
61
62         aaa = (char *)malloc((size_t)(strlen(arg) + 6));
63         if (!aaa) return -1;
64
65         sprintf(aaa, "ECHO %s", arg);
66         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
67         free(aaa);
68         return ret;
69 }
70
71
72 /*
73  * Asks the server to close the connecction.
74  * Should always return 200.
75  */
76 int CtdlIPCQuit(void)
77 {
78         register int ret;
79         char aaa[128];
80
81         netio_lock();
82         serv_puts("QUIT");
83         serv_gets(aaa);
84         ret = atoi(aaa);
85         netio_unlock();
86         return ret;
87 }
88
89
90 /*
91  * Asks the server to logout.  Should always return 200, even if no user
92  * was logged in.  The user will not be logged in after this!
93  */
94 int CtdlIPCLogout(void)
95 {
96         register int ret;
97         char aaa[128];
98
99         netio_lock();
100         serv_puts("LOUT");
101         serv_gets(aaa);
102         ret = atoi(aaa);
103         netio_unlock();
104         return ret;
105 }
106
107
108 /*
109  * First stage of authentication - pass the username.  Returns 300 if the
110  * username is able to log in, with the username correctly spelled in cret.
111  * Returns various 500 error codes if the user doesn't exist, etc.
112  */
113 int CtdlIPCTryLogin(const char *username, char *cret)
114 {
115         register int ret;
116         char *aaa;
117
118         if (!username) return -2;
119         if (!cret) return -2;
120
121         aaa = (char *)malloc((size_t)(strlen(username) + 6));
122         if (!aaa) return -1;
123
124         sprintf(aaa, "USER %s", username);
125         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
126         free(aaa);
127         return ret;
128 }
129
130
131 /*
132  * Second stage of authentication - provide password.  The server returns
133  * 200 and several arguments in cret relating to the user's account.
134  */
135 int CtdlIPCTryPassword(const char *passwd, char *cret)
136 {
137         register int ret;
138         char *aaa;
139
140         if (!passwd) return -2;
141         if (!cret) return -2;
142
143         aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
144         if (!aaa) return -1;
145
146         sprintf(aaa, "PASS %s", passwd);
147         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
148         free(aaa);
149         return ret;
150 }
151
152
153 /*
154  * Create a new user.  This returns 200 plus the same arguments as TryPassword
155  * if selfservice is nonzero, unless there was a problem creating the account.
156  * If selfservice is zero, creates a new user but does not log out the existing
157  * user - intended for use by system administrators to create accounts on
158  * behalf of other users.
159  */
160 int CtdlIPCCreateUser(const char *username, int selfservice, char *cret)
161 {
162         register int ret;
163         char *aaa;
164
165         if (!username) return -2;
166         if (!cret) return -2;
167
168         aaa = (char *)malloc((size_t)(strlen(username) + 6));
169         if (!aaa) return -1;
170
171         sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU",  username);
172         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
173         free(aaa);
174         return ret;
175 }
176
177
178 /*
179  * Changes the user's password.  Returns 200 if changed, errors otherwise.
180  */
181 int CtdlIPCChangePassword(const char *passwd, char *cret)
182 {
183         register int ret;
184         char *aaa;
185
186         if (!passwd) return -2;
187         if (!cret) return -2;
188
189         aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
190         if (!aaa) return -1;
191
192         sprintf(aaa, "SETP %s", passwd);
193         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
194         free(aaa);
195         return ret;
196 }
197
198
199 /* LKRN */
200 /* Caller must free the march list */
201 /* which is 0 = LRMS, 1 = LKRN, 2 = LKRO, 3 = LKRA, 4 = LZRM */
202 /* floor is -1 for all, or floornum */
203 int CtdlIPCKnownRooms(int which, int floor, struct march **listing, char *cret)
204 {
205         register int ret;
206         struct march *march = NULL;
207         static char *proto[] = {"LRMS", "LKRN", "LKRO", "LKRA", "LZRM" };
208         char aaa[SIZ];
209         char *bbb = NULL;
210         size_t bbbsize;
211
212         if (!listing) return -2;
213         if (*listing) return -2;        /* Free the listing first */
214         if (!cret) return -2;
215         if (which < 0 || which > 4) return -2;
216         if (floor < -1) return -2;      /* Can't validate upper bound, sorry */
217
218         sprintf(aaa, "%s %d", proto[which], floor);
219         ret = CtdlIPCGenericCommand(aaa, NULL, 0, &bbb, &bbbsize, cret);
220         if (ret / 100 == 1) {
221                 struct march *mptr;
222
223                 while (bbb && strlen(bbb)) {
224                         int a;
225
226                         extract_token(aaa, bbb, 0, '\n');
227                         a = strlen(aaa);
228                         memmove(bbb, bbb + a + 1, strlen(bbb) - a);
229                         mptr = (struct march *) malloc(sizeof (struct march));
230                         if (mptr) {
231                                 mptr->next = NULL;
232                                 extract(mptr->march_name, aaa, 0);
233                                 mptr->march_floor = (char) extract_int(aaa, 2);
234                                 mptr->march_order = (char) extract_int(aaa, 3);
235                                 if (march == NULL)
236                                         march = mptr;
237                                 else {
238                                         struct march *mptr2;
239
240                                         mptr2 = march;
241                                         while (mptr2->next != NULL)
242                                                 mptr2 = mptr2->next;
243                                         mptr2->next = mptr;
244                                 }
245                         }
246                 }
247         }
248         *listing = march;
249         return ret;
250 }
251
252
253 /* GETU */
254 /* Caller must free the struct usersupp; caller may pass an existing one */
255 int CtdlIPCGetConfig(struct usersupp **uret, char *cret)
256 {
257         register int ret;
258
259         if (!cret) return -2;
260         if (!uret) return -2;
261         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof (struct usersupp));
262         if (!*uret) return -1;
263
264         ret = CtdlIPCGenericCommand("GETU", NULL, 0, NULL, NULL, cret);
265         if (ret / 100 == 2) {
266                 uret[0]->USscreenwidth = extract_int(cret, 0);
267                 uret[0]->USscreenheight = extract_int(cret, 1);
268                 uret[0]->flags = extract_int(cret, 2);
269         }
270         return ret;
271 }
272
273
274 /* SETU */
275 int CtdlIPCSetConfig(struct usersupp *uret, char *cret)
276 {
277         char aaa[48];
278
279         if (!uret) return -2;
280         if (!cret) return -2;
281
282         sprintf(aaa, "SETU %d|%d|%d",
283                         uret->USscreenwidth, uret->USscreenheight,
284                         uret->flags);
285         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
286 }
287
288
289 /* GOTO */
290 int CtdlIPCGotoRoom(const char *room, const char *passwd,
291                 struct ctdlipcroom **rret, char *cret)
292 {
293         register int ret;
294         char *aaa;
295
296         if (!cret) return -2;
297         if (!rret) return -2;
298         if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
299         if (!*rret) return -1;
300
301         if (passwd) {
302                 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
303                 if (!aaa) {
304                         free(*rret);
305                         return -1;
306                 }
307                 sprintf(aaa, "GOTO %s|%s", room, passwd);
308         } else {
309                 aaa = (char *)malloc(strlen(room) + 6);
310                 if (!aaa) {
311                         free(*rret);
312                         return -1;
313                 }
314                 sprintf(aaa, "GOTO %s", room);
315         }
316         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
317         if (ret / 100 == 2) {
318                 extract(rret[0]->RRname, cret, 0);
319                 rret[0]->RRunread = extract_long(cret, 1);
320                 rret[0]->RRtotal = extract_long(cret, 2);
321                 rret[0]->RRinfoupdated = extract_int(cret, 3);
322                 rret[0]->RRflags = extract_int(cret, 4);
323                 rret[0]->RRhighest = extract_long(cret, 5);
324                 rret[0]->RRlastread = extract_long(cret, 6);
325                 rret[0]->RRismailbox = extract_int(cret, 7);
326                 rret[0]->RRaide = extract_int(cret, 8);
327                 rret[0]->RRnewmail = extract_long(cret, 9);
328                 rret[0]->RRfloor = extract_int(cret, 10);
329         } else {
330                 free(*rret);
331         }
332         return ret;
333 }
334
335
336 /* MSGS */
337 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
338 /* whicharg is number of messages, applies to last, first, gt, lt */
339 int CtdlIPCGetMessages(int which, int whicharg, const char *template,
340                 long **mret, char *cret)
341 {
342         register int ret;
343         register long count = 0;
344         static char *proto[] =
345                 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
346         char aaa[33];
347         char *bbb;
348         size_t bbbsize;
349
350         if (!cret) return -2;
351         if (!mret) return -2;
352         if (*mret) return -2;
353         if (which < 0 || which > 6) return -2;
354
355         if (which <= 2)
356                 sprintf(aaa, "MSGS %s||%d", proto[which],
357                                 (template) ? 1 : 0);
358         else
359                 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
360                                 (template) ? 1 : 0);
361         if (template) count = strlen(template);
362         ret = CtdlIPCGenericCommand(aaa, template, count, &bbb, &bbbsize, cret);
363         count = 0;
364         while (strlen(bbb)) {
365                 int a;
366
367                 extract_token(aaa, bbb, 0, '\n');
368                 a = strlen(aaa);
369                 memmove(aaa, bbb + a + 1, strlen(bbb) - a - 1);
370                 *mret = (long *)realloc(mret,
371                                         (size_t)((count + 1) * sizeof (long)));
372                 if (*mret)
373                         *mret[count++] = atol(aaa);
374                 *mret[count] = 0L;
375         }
376         return ret;
377 }
378
379
380 /* MSG0, MSG2 */
381 int CtdlIPCGetSingleMessage(long msgnum, int headers, int as_mime,
382                 struct ctdlipcmessage **mret, char *cret)
383 {
384         register int ret;
385         char aaa[SIZ];
386         char *bbb = NULL;
387         size_t bbbsize;
388         int multipart_hunting = 0;
389         char multipart_prefix[SIZ];
390
391         if (!cret) return -1;
392         if (!mret) return -1;
393         if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
394         if (!*mret) return -1;
395         if (!msgnum) return -1;
396
397         strcpy(mret[0]->content_type, "");
398         sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
399         ret = CtdlIPCGenericCommand(aaa, NULL, 0, &bbb, &bbbsize, cret);
400         if (ret / 100 == 1) {
401                 if (as_mime != 2) {
402                         strcpy(mret[0]->mime_chosen, "1");      /* Default chosen-part is "1" */
403                         while (strlen(bbb) > 4 && bbb[4] == '=') {
404                                 extract_token(aaa, bbb, 0, '\n');
405                                 remove_token(bbb, 0, '\n');
406
407                                 if (!strncasecmp(aaa, "nhdr=yes", 8))
408                                         mret[0]->nhdr = 1;
409                                 else if (!strncasecmp(aaa, "from=", 5))
410                                         strcpy(mret[0]->author, &aaa[5]);
411                                 else if (!strncasecmp(aaa, "type=", 5))
412                                         mret[0]->type = atoi(&aaa[5]);
413                                 else if (!strncasecmp(aaa, "msgn=", 5))
414                                         strcpy(mret[0]->msgid, &aaa[5]);
415                                 else if (!strncasecmp(aaa, "subj=", 5))
416                                         strcpy(mret[0]->subject, &aaa[5]);
417                                 else if (!strncasecmp(aaa, "rfca=", 5))
418                                         strcpy(mret[0]->email, &aaa[5]);
419                                 else if (!strncasecmp(aaa, "hnod=", 5))
420                                         strcpy(mret[0]->hnod, &aaa[5]);
421                                 else if (!strncasecmp(aaa, "room=", 5))
422                                         strcpy(mret[0]->room, &aaa[5]);
423                                 else if (!strncasecmp(aaa, "node=", 5))
424                                         strcpy(mret[0]->node, &aaa[5]);
425                                 else if (!strncasecmp(aaa, "rcpt=", 5))
426                                         strcpy(mret[0]->recipient, &aaa[5]);
427                                 else if (!strncasecmp(aaa, "time=", 5))
428                                         mret[0]->time = atol(&aaa[5]);
429
430                                 /* Multipart/alternative prefix & suffix strings help
431                                  * us to determine which part we want to download.
432                                  */
433                                 else if (!strncasecmp(aaa, "pref=", 5)) {
434                                         extract(multipart_prefix, &aaa[5], 1);
435                                         if (!strcasecmp(multipart_prefix,
436                                            "multipart/alternative")) {
437                                                 ++multipart_hunting;
438                                         }
439                                 }
440                                 else if (!strncasecmp(aaa, "suff=", 5)) {
441                                         extract(multipart_prefix, &aaa[5], 1);
442                                         if (!strcasecmp(multipart_prefix,
443                                            "multipart/alternative")) {
444                                                 ++multipart_hunting;
445                                         }
446                                 }
447
448                                 else if (!strncasecmp(aaa, "part=", 5)) {
449                                         struct parts *ptr, *chain;
450         
451                                         ptr = (struct parts *)calloc(1, sizeof (struct parts));
452                                         if (ptr) {
453
454                                                 /* Fill the buffers for the caller */
455                                                 extract(ptr->name, &aaa[5], 0);
456                                                 extract(ptr->filename, &aaa[5], 1);
457                                                 extract(ptr->number, &aaa[5], 2);
458                                                 extract(ptr->disposition, &aaa[5], 3);
459                                                 extract(ptr->mimetype, &aaa[5], 4);
460                                                 ptr->length = extract_long(&aaa[5], 5);
461                                                 if (!mret[0]->attachments)
462                                                         mret[0]->attachments = ptr;
463                                                 else {
464                                                         chain = mret[0]->attachments;
465                                                         while (chain->next)
466                                                                 chain = chain->next;
467                                                         chain->next = ptr;
468                                                 }
469
470                                                 /* Now handle multipart/alternative */
471                                                 if (multipart_hunting > 0) {
472                                                         if ( (!strcasecmp(ptr->mimetype,
473                                                              "text/plain"))
474                                                            || (!strcasecmp(ptr->mimetype,
475                                                               "text/html")) ) {
476                                                                 strcpy(mret[0]->mime_chosen,
477                                                                         ptr->number);
478                                                         }
479                                                 }
480
481                                         }
482                                 }
483                         }
484                         /* Eliminate "text\n" */
485                         remove_token(bbb, 0, '\n');
486
487                         /* If doing a MIME thing, pull out the extra headers */
488                         if (as_mime == 4) {
489                                 do {
490                                         if (!strncasecmp(bbb, "Content-type: ", 14)) {
491                                                 extract_token(mret[0]->content_type, bbb, 0, '\n');
492                                                 strcpy(mret[0]->content_type,
493                                                         &mret[0]->content_type[14]);
494                                                 striplt(mret[0]->content_type);
495                                         }
496                                         remove_token(bbb, 0, '\n');
497                                 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
498                         }
499
500
501                 }
502                 if (strlen(bbb)) {
503                         /* Strip trailing whitespace */
504                         bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
505                 } else {
506                         bbb = (char *)realloc(bbb, 1);
507                         *bbb = '\0';
508                 }
509                 mret[0]->text = bbb;
510         }
511         return ret;
512 }
513
514
515 /* WHOK */
516 int CtdlIPCWhoKnowsRoom(char **listing, char *cret)
517 {
518         register int ret;
519         size_t bytes;
520
521         if (!cret) return -2;
522         if (!listing) return -2;
523         if (*listing) return -2;
524
525         ret = CtdlIPCGenericCommand("WHOK", NULL, 0, listing, &bytes, cret);
526         return ret;
527 }
528
529
530 /* INFO */
531 int CtdlIPCServerInfo(struct CtdlServInfo *ServInfo, char *cret)
532 {
533         register int ret;
534         size_t bytes;
535         char *listing = NULL;
536         char buf[SIZ];
537
538         if (!cret) return -2;
539         if (!ServInfo) return -2;
540
541         ret = CtdlIPCGenericCommand("INFO", NULL, 0, &listing, &bytes, cret);
542         if (ret / 100 == 1) {
543                 int line = 0;
544
545                 while (*listing && strlen(listing)) {
546                         extract_token(buf, listing, 0, '\n');
547                         remove_token(listing, 0, '\n');
548                         switch (line++) {
549                         case 0:         ServInfo->serv_pid = atoi(buf);
550                                         break;
551                         case 1:         strcpy(ServInfo->serv_nodename,buf);
552                                         break;
553                         case 2:         strcpy(ServInfo->serv_humannode,buf);
554                                         break;
555                         case 3:         strcpy(ServInfo->serv_fqdn,buf);
556                                         break;
557                         case 4:         strcpy(ServInfo->serv_software,buf);
558                                         break;
559                         case 5:         ServInfo->serv_rev_level = atoi(buf);
560                                         break;
561                         case 6:         strcpy(ServInfo->serv_bbs_city,buf);
562                                         break;
563                         case 7:         strcpy(ServInfo->serv_sysadm,buf);
564                                         break;
565                         case 9:         strcpy(ServInfo->serv_moreprompt,buf);
566                                         break;
567                         case 10:        ServInfo->serv_ok_floors = atoi(buf);
568                                         break;
569                         case 11:        ServInfo->serv_paging_level = atoi(buf);
570                                         break;
571                         case 13:        ServInfo->serv_supports_qnop = atoi(buf);
572                                         break;
573                         }
574                 }
575
576         }
577         return ret;
578 }
579
580
581 /* RDIR */
582 int CtdlIPCReadDirectory(char **listing, char *cret)
583 {
584         register int ret;
585         size_t bytes;
586
587         if (!cret) return -2;
588         if (!listing) return -2;
589         if (*listing) return -2;
590
591         ret = CtdlIPCGenericCommand("RDIR", NULL, 0, listing, &bytes, cret);
592         return ret;
593 }
594
595
596 /*
597  * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
598  */
599 int CtdlIPCSetLastRead(long msgnum, char *cret)
600 {
601         register int ret;
602         char aaa[16];
603
604         if (!cret) return -2;
605
606         if (msgnum)
607                 sprintf(aaa, "SLRP %ld", msgnum);
608         else
609                 sprintf(aaa, "SLRP HIGHEST");
610         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
611         return ret;
612 }
613
614
615 /* INVT */
616 int CtdlIPCInviteUserToRoom(const char *username, char *cret)
617 {
618         register int ret;
619         char *aaa;
620
621         if (!cret) return -2;
622         if (!username) return -2;
623
624         aaa = (char *)malloc(strlen(username) + 6);
625         if (!aaa) return -1;
626
627         sprintf(aaa, "INVT %s", username);
628         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
629         free(aaa);
630         return ret;
631 }
632
633
634 /* KICK */
635 int CtdlIPCKickoutUserFromRoom(const char *username, char *cret)
636 {
637         register int ret;
638         char *aaa;
639
640         if (!cret) return -1;
641         if (!username) return -1;
642
643         aaa = (char *)malloc(strlen(username) + 6);
644
645         sprintf(aaa, "KICK %s", username);
646         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
647         free(aaa);
648         return ret;
649 }
650
651
652 /* GETR */
653 int CtdlIPCGetRoomAttributes(struct quickroom **qret, char *cret)
654 {
655         register int ret;
656
657         if (!cret) return -2;
658         if (!qret) return -2;
659         if (!*qret) *qret = (struct quickroom *)calloc(1, sizeof (struct quickroom));
660         if (!*qret) return -1;
661
662         ret = CtdlIPCGenericCommand("GETR", NULL, 0, NULL, NULL, cret);
663         if (ret / 100 == 2) {
664                 extract(qret[0]->QRname, cret, 0);
665                 extract(qret[0]->QRpasswd, cret, 1);
666                 extract(qret[0]->QRdirname, cret, 2);
667                 qret[0]->QRflags = extract_int(cret, 3);
668                 qret[0]->QRfloor = extract_int(cret, 4);
669                 qret[0]->QRorder = extract_int(cret, 5);
670         }
671         return ret;
672 }
673
674
675 /* SETR */
676 /* set forget to kick all users out of room */
677 int CtdlIPCSetRoomAttributes(int forget, struct quickroom *qret, char *cret)
678 {
679         register int ret;
680         char *aaa;
681
682         if (!cret) return -2;
683         if (!qret) return -2;
684
685         aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
686                         strlen(qret->QRdirname) + 52);
687         if (!aaa) return -1;
688
689         sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d",
690                         qret->QRname, qret->QRpasswd, qret->QRdirname,
691                         qret->QRflags, forget, qret->QRfloor, qret->QRorder);
692         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
693         free(aaa);
694         return ret;
695 }
696
697
698 /* GETA */
699 int CtdlIPCGetRoomAide(char *cret)
700 {
701         if (!cret) return -1;
702
703         return CtdlIPCGenericCommand("GETA", NULL, 0, NULL, NULL, cret);
704 }
705
706
707 /* SETA */
708 int CtdlIPCSetRoomAide(const char *username, char *cret)
709 {
710         register int ret;
711         char *aaa;
712
713         if (!cret) return -2;
714         if (!username) return -2;
715
716         aaa = (char *)malloc(strlen(username) + 6);
717         if (!aaa) return -1;
718
719         sprintf(aaa, "SETA %s", username);
720         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
721         free(aaa);
722         return ret;
723 }
724
725
726 /* ENT0 */
727 int CtdlIPCPostMessage(int flag, const struct ctdlipcmessage *mr, char *cret)
728 {
729         register int ret;
730         char *aaa;
731
732         if (!cret) return -2;
733         if (!mr) return -2;
734
735         aaa = (char *)malloc(strlen(mr->recipient) + strlen(mr->author) + 40);
736         if (!aaa) return -1;
737
738         sprintf(aaa, "ENT0 %d|%s|%d|%d|%s", flag, mr->recipient, mr->anonymous,
739                         mr->type, mr->author);
740         ret = CtdlIPCGenericCommand(aaa, mr->text, strlen(mr->text), NULL,
741                         NULL, cret);
742         free(aaa);
743         return ret;
744 }
745
746
747 /* RINF */
748 int CtdlIPCRoomInfo(char **iret, char *cret)
749 {
750         size_t bytes;
751
752         if (!cret) return -2;
753         if (!iret) return -2;
754         if (*iret) return -2;
755
756         return CtdlIPCGenericCommand("RINF", NULL, 0, iret, &bytes, cret);
757 }
758
759
760 /* DELE */
761 int CtdlIPCDeleteMessage(long msgnum, char *cret)
762 {
763         char aaa[16];
764
765         if (!cret) return -2;
766         if (!msgnum) return -2;
767
768         sprintf(aaa, "DELE %ld", msgnum);
769         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
770 }
771
772
773 /* MOVE */
774 int CtdlIPCMoveMessage(int copy, long msgnum, const char *destroom, char *cret)
775 {
776         register int ret;
777         char *aaa;
778
779         if (!cret) return -2;
780         if (!destroom) return -2;
781         if (!msgnum) return -2;
782
783         aaa = (char *)malloc(strlen(destroom) + 28);
784         if (!aaa) return -1;
785
786         sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
787         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
788         free(aaa);
789         return ret;
790 }
791
792
793 /* KILL */
794 int CtdlIPCDeleteRoom(int for_real, char *cret)
795 {
796         char aaa[16];
797
798         if (!cret) return -2;
799
800         sprintf(aaa, "KILL %d", for_real);
801         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
802 }
803
804
805 /* CRE8 */
806 int CtdlIPCCreateRoom(int for_real, const char *roomname, int type,
807                 const char *password, int floor, char *cret)
808 {
809         register int ret;
810         char *aaa;
811
812         if (!cret) return -2;
813         if (!roomname) return -2;
814
815         if (password) {
816                 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
817                 if (!aaa) return -1;
818                 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
819                                 password, floor);
820         } else {
821                 aaa = (char *)malloc(strlen(roomname) + 40);
822                 if (!aaa) return -1;
823                 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
824                                 floor);
825         }
826         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
827         free(aaa);
828         return ret;
829 }
830
831
832 /* FORG */
833 int CtdlIPCForgetRoom(char *cret)
834 {
835         if (!cret) return -2;
836
837         return CtdlIPCGenericCommand("FORG", NULL, 0, NULL, NULL, cret);
838 }
839
840
841 /* MESG */
842 int CtdlIPCSystemMessage(const char *message, char **mret, char *cret)
843 {
844         register int ret;
845         char *aaa;
846         size_t bytes;
847
848         if (!cret) return -2;
849         if (!mret) return -2;
850         if (*mret) return -2;
851         if (!message) return -2;
852
853         aaa = (char *)malloc(strlen(message) + 6);
854         if (!aaa) return -1;
855
856         sprintf(aaa, "MESG %s", message);
857         ret = CtdlIPCGenericCommand(aaa, NULL, 0, mret, &bytes, cret);
858         free(aaa);
859         return ret;
860 }
861
862
863 /* GNUR */
864 int CtdlIPCNextUnvalidatedUser(char *cret)
865 {
866         if (!cret) return -2;
867
868         return CtdlIPCGenericCommand("GNUR", NULL, 0, NULL, NULL, cret);
869 }
870
871
872 /* GREG */
873 int CtdlIPCGetUserRegistration(const char *username, char **rret, char *cret)
874 {
875         register int ret;
876         char *aaa;
877         size_t bytes;
878
879         if (!cret) return -2;
880         if (!rret) return -2;
881         if (*rret) return -2;
882
883         if (username)
884                 aaa = (char *)malloc(strlen(username) + 6);
885         else
886                 aaa = (char *)malloc(12);
887         if (!aaa) return -1;
888
889         if (username)
890                 sprintf(aaa, "GREG %s", username);
891         else
892                 sprintf(aaa, "GREG _SELF_");
893         ret = CtdlIPCGenericCommand(aaa, NULL, 0, rret, &bytes, cret);
894         free(aaa);
895         return ret;
896 }
897
898
899 /* VALI */
900 int CtdlIPCValidateUser(const char *username, int axlevel, char *cret)
901 {
902         register int ret;
903         char *aaa;
904
905         if (!cret) return -2;
906         if (!username) return -2;
907         if (axlevel < 0 || axlevel > 7) return -2;
908
909         aaa = (char *)malloc(strlen(username) + 17);
910         if (!aaa) return -1;
911
912         sprintf(aaa, "VALI %s|%d", username, axlevel);
913         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
914         free(aaa);
915         return ret;
916 }
917
918
919 /* EINF */
920 int CtdlIPCSetRoomInfo(int for_real, const char *info, char *cret)
921 {
922         char aaa[16];
923
924         if (!cret) return -1;
925         if (!info) return -1;
926
927         sprintf(aaa, "EINF %d", for_real);
928         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
929 }
930
931
932 /* LIST */
933 int CtdlIPCUserListing(char **listing, char *cret)
934 {
935         size_t bytes;
936
937         if (!cret) return -1;
938         if (!listing) return -1;
939         if (*listing) return -1;
940
941         return CtdlIPCGenericCommand("LIST", NULL, 0, listing, &bytes, cret);
942 }
943
944
945 /* REGI */
946 int CtdlIPCSetRegistration(const char *info, char *cret)
947 {
948         if (!cret) return -1;
949         if (!info) return -1;
950
951         return CtdlIPCGenericCommand("REGI", info, strlen(info),
952                         NULL, NULL, cret);
953 }
954
955
956 /* CHEK */
957 int CtdlIPCMiscCheck(struct ctdlipcmisc *chek, char *cret)
958 {
959         register int ret;
960
961         if (!cret) return -1;
962         if (!chek) return -1;
963
964         ret = CtdlIPCGenericCommand("CHEK", NULL, 0, NULL, NULL, cret);
965         if (ret / 100 == 2) {
966                 chek->newmail = extract_long(cret, 0);
967                 chek->needregis = extract_int(cret, 1);
968                 chek->needvalid = extract_int(cret, 2);
969         }
970         return ret;
971 }
972
973
974 /* DELF */
975 int CtdlIPCDeleteFile(const char *filename, char *cret)
976 {
977         register int ret;
978         char *aaa;
979
980         if (!cret) return -2;
981         if (!filename) return -2;
982         
983         aaa = (char *)malloc(strlen(filename) + 6);
984         if (!aaa) return -1;
985
986         sprintf(aaa, "DELF %s", filename);
987         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
988         free(aaa);
989         return ret;
990 }
991
992
993 /* MOVF */
994 int CtdlIPCMoveFile(const char *filename, const char *destroom, char *cret)
995 {
996         register int ret;
997         char *aaa;
998
999         if (!cret) return -2;
1000         if (!filename) return -2;
1001         if (!destroom) return -2;
1002
1003         aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1004         if (!aaa) return -1;
1005
1006         sprintf(aaa, "MOVF %s|%s", filename, destroom);
1007         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1008         free(aaa);
1009         return ret;
1010 }
1011
1012
1013 /* NETF */
1014 int CtdlIPCNetSendFile(const char *filename, const char *destnode, char *cret)
1015 {
1016         register int ret;
1017         char *aaa;
1018
1019         if (!cret) return -2;
1020         if (!filename) return -2;
1021         if (!destnode) return -2;
1022
1023         aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1024         if (!aaa) return -1;
1025
1026         sprintf(aaa, "NETF %s|%s", filename, destnode);
1027         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1028         free(aaa);
1029         return ret;
1030 }
1031
1032
1033 /* RWHO */
1034 int CtdlIPCOnlineUsers(char **listing, time_t *stamp, char *cret)
1035 {
1036         register int ret;
1037         size_t bytes;
1038
1039         if (!cret) return -1;
1040         if (!listing) return -1;
1041         if (*listing) return -1;
1042
1043         *stamp = CtdlIPCServerTime(cret);
1044         if (!*stamp)
1045                 *stamp = time(NULL);
1046         ret = CtdlIPCGenericCommand("RWHO", NULL, 0, listing, &bytes, cret);
1047         return ret;
1048 }
1049
1050
1051 /* OPEN */
1052 int CtdlIPCFileDownload(const char *filename, void **buf, char *cret)
1053 {
1054         register int ret;
1055         size_t bytes;
1056         time_t last_mod;
1057         char mimetype[SIZ];
1058         char *aaa;
1059
1060         if (!cret) return -2;
1061         if (!filename) return -2;
1062         if (!buf) return -2;
1063         if (*buf) return -2;
1064         if (download_in_progress) return -2;
1065
1066         aaa = (char *)malloc(strlen(filename) + 6);
1067         if (!aaa) return -1;
1068
1069         sprintf(aaa, "OPEN %s", filename);
1070         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1071         free(aaa);
1072         /* FIXME: Possible race condition */
1073         if (ret / 100 == 2) {
1074                 download_in_progress = 1;
1075                 bytes = extract_long(cret, 0);
1076                 last_mod = extract_int(cret, 1);
1077                 extract(mimetype, cret, 2);
1078                 ret = CtdlIPCReadDownload(buf, bytes, cret);
1079                 ret = CtdlIPCEndDownload(cret);
1080                 if (ret / 100 == 2)
1081                         sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
1082                                         filename, mimetype);
1083         }
1084         return ret;
1085 }
1086
1087
1088 /* OPNA */
1089 int CtdlIPCAttachmentDownload(long msgnum, const char *part, void **buf,
1090                 char *cret)
1091 {
1092         register int ret;
1093         size_t bytes;
1094         time_t last_mod;
1095         char filename[SIZ];
1096         char mimetype[SIZ];
1097         char *aaa;
1098
1099         if (!cret) return -2;
1100         if (!buf) return -2;
1101         if (*buf) return -2;
1102         if (!part) return -2;
1103         if (!msgnum) return -2;
1104         if (download_in_progress) return -2;
1105
1106         aaa = (char *)malloc(strlen(part) + 17);
1107         if (!aaa) return -1;
1108
1109         sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1110         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1111         free(aaa);
1112         /* FIXME: Possible race condition */
1113         if (ret / 100 == 2) {
1114                 download_in_progress = 1;
1115                 bytes = extract_long(cret, 0);
1116                 last_mod = extract_int(cret, 1);
1117                 extract(mimetype, cret, 2);
1118                 ret = CtdlIPCReadDownload(buf, bytes, cret);
1119                 ret = CtdlIPCEndDownload(cret);
1120                 if (ret / 100 == 2)
1121                         sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
1122                                         filename, mimetype);
1123         }
1124         return ret;
1125 }
1126
1127
1128 /* OIMG */
1129 int CtdlIPCImageDownload(const char *filename, void **buf, char *cret)
1130 {
1131         register int ret;
1132         size_t bytes;
1133         time_t last_mod;
1134         char mimetype[SIZ];
1135         char *aaa;
1136
1137         if (!cret) return -1;
1138         if (!buf) return -1;
1139         if (*buf) return -1;
1140         if (!filename) return -1;
1141         if (download_in_progress) return -1;
1142
1143         aaa = (char *)malloc(strlen(filename) + 6);
1144         if (!aaa) return -1;
1145
1146         sprintf(aaa, "OIMG %s", filename);
1147         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1148         free(aaa);
1149         /* FIXME: Possible race condition */
1150         if (ret / 100 == 2) {
1151                 download_in_progress = 1;
1152                 bytes = extract_long(cret, 0);
1153                 last_mod = extract_int(cret, 1);
1154                 extract(mimetype, cret, 2);
1155                 ret = CtdlIPCReadDownload(buf, bytes, cret);
1156                 ret = CtdlIPCEndDownload(cret);
1157                 if (ret / 100 == 2)
1158                         sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
1159                                         filename, mimetype);
1160         }
1161         return ret;
1162 }
1163
1164
1165 /* UOPN */
1166 int CtdlIPCFileUpload(const char *filename, const char *comment, void *buf,
1167                 size_t bytes, char *cret)
1168 {
1169         register int ret;
1170         char *aaa;
1171
1172         if (!cret) return -1;
1173         if (!filename) return -1;
1174         if (!comment) return -1;
1175         if (upload_in_progress) return -1;
1176
1177         aaa = (char *)malloc(strlen(filename) + strlen(comment) + 7);
1178         if (!aaa) return -1;
1179
1180         sprintf(aaa, "UOPN %s|%s", filename, comment);
1181         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1182         free(aaa);
1183         /* FIXME: Possible race condition */
1184         if (ret / 100 == 2)
1185                 upload_in_progress = 1;
1186         ret = CtdlIPCWriteUpload(buf, bytes, cret);
1187         ret = CtdlIPCEndUpload(cret);
1188         return ret;
1189 }
1190
1191
1192 /* UIMG */
1193 int CtdlIPCImageUpload(int for_real, const char *filename, size_t bytes,
1194                 char *cret)
1195 {
1196         register int ret;
1197         char *aaa;
1198
1199         if (!cret) return -1;
1200         if (!filename) return -1;
1201         if (upload_in_progress) return -1;
1202
1203         aaa = (char *)malloc(strlen(filename) + 17);
1204         if (!aaa) return -1;
1205
1206         sprintf(aaa, "UIMG %d|%s", for_real, filename);
1207         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1208         free(aaa);
1209         /* FIXME: Possible race condition */
1210         if (ret / 100 == 2)
1211                 upload_in_progress = 1;
1212         return ret;
1213 }
1214
1215
1216 /* QUSR */
1217 int CtdlIPCQueryUsername(const char *username, char *cret)
1218 {
1219         register int ret;
1220         char *aaa;
1221
1222         if (!cret) return -2;
1223         if (!username) return -2;
1224
1225         aaa = (char *)malloc(strlen(username) + 6);
1226         if (!aaa) return -1;
1227
1228         sprintf(aaa, "QUSR %s", username);
1229         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1230         free(aaa);
1231         return ret;
1232 }
1233
1234
1235 /* LFLR */
1236 int CtdlIPCFloorListing(char **listing, char *cret)
1237 {
1238         size_t bytes;
1239
1240         if (!cret) return -2;
1241         if (!listing) return -2;
1242         if (*listing) return -2;
1243
1244         return CtdlIPCGenericCommand("LFLR", NULL, 0, listing, &bytes, cret);
1245 }
1246
1247
1248 /* CFLR */
1249 int CtdlIPCCreateFloor(int for_real, const char *name, char *cret)
1250 {
1251         register int ret;
1252         char *aaa;
1253
1254         if (!cret) return -2;
1255         if (!name) return -2;
1256
1257         aaa = (char *)malloc(strlen(name) + 17);
1258         if (!aaa) return -1;
1259
1260         sprintf(aaa, "CFLR %s|%d", name, for_real);
1261         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1262         free(aaa);
1263         return ret;
1264 }
1265
1266
1267 /* KFLR */
1268 int CtdlIPCDeleteFloor(int for_real, int floornum, char *cret)
1269 {
1270         char aaa[27];
1271
1272         if (!cret) return -1;
1273         if (floornum < 0) return -1;
1274
1275         sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1276         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1277 }
1278
1279
1280 /* EFLR */
1281 int CtdlIPCEditFloor(int floornum, const char *floorname, char *cret)
1282 {
1283         register int ret;
1284         char *aaa;
1285
1286         if (!cret) return -2;
1287         if (!floorname) return -2;
1288         if (floornum < 0) return -2;
1289
1290         aaa = (char *)malloc(strlen(floorname) + 17);
1291         if (!aaa) return -1;
1292
1293         sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1294         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1295         free(aaa);
1296         return ret;
1297 }
1298
1299
1300 /* IDEN */
1301 int CtdlIPCIdentifySoftware(int developerid, int clientid, int revision,
1302                 const char *software_name, const char *hostname, char *cret)
1303 {
1304         register int ret;
1305         char *aaa;
1306
1307         if (developerid < 0) return -2;
1308         if (clientid < 0) return -2;
1309         if (revision < 0) return -2;
1310         if (!software_name) return -2;
1311         if (!hostname) return -2;
1312
1313         aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1314         if (!aaa) return -1;
1315
1316         sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1317                         revision, software_name, hostname);
1318         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1319         free(aaa);
1320         return ret;
1321 }
1322
1323
1324 /* SEXP */
1325 int CtdlIPCSendInstantMessage(const char *username, const char *text,
1326                 char *cret)
1327 {
1328         register int ret;
1329         char *aaa;
1330
1331         if (!cret) return -2;
1332         if (!username) return -2;
1333
1334         aaa = (char *)malloc(strlen(username) + 8);
1335         if (!aaa) return -1;
1336
1337         if (text) {
1338                 sprintf(aaa, "SEXP %s|-", username);
1339                 ret = CtdlIPCGenericCommand(aaa, text, strlen(text),
1340                                 NULL, NULL, cret);
1341         } else {
1342                 sprintf(aaa, "SEXP %s||", username);
1343                 ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1344         }
1345         free(aaa);
1346         return ret;
1347 }
1348
1349
1350 /* GEXP */
1351 int CtdlIPCGetInstantMessage(char **listing, char *cret)
1352 {
1353         size_t bytes;
1354
1355         if (!cret) return -2;
1356         if (!listing) return -2;
1357         if (*listing) return -2;
1358
1359         return CtdlIPCGenericCommand("GEXP", NULL, 0, listing, &bytes, cret);
1360 }
1361
1362
1363 /* DEXP */
1364 /* mode is 0 = enable, 1 = disable, 2 = status */
1365 int CtdlIPCEnableInstantMessageReceipt(int mode, char *cret)
1366 {
1367         char aaa[16];
1368
1369         if (!cret) return -2;
1370
1371         sprintf(aaa, "DEXP %d", mode);
1372         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1373 }
1374
1375
1376 /* EBIO */
1377 int CtdlIPCSetBio(char *bio, char *cret)
1378 {
1379         if (!cret) return -2;
1380         if (!bio) return -2;
1381
1382         return CtdlIPCGenericCommand("EBIO", bio, strlen(bio),
1383                         NULL, NULL, cret);
1384 }
1385
1386
1387 /* RBIO */
1388 int CtdlIPCGetBio(const char *username, char **listing, char *cret)
1389 {
1390         register int ret;
1391         size_t bytes;
1392         char *aaa;
1393
1394         if (!cret) return -2;
1395         if (!username) return -2;
1396         if (!listing) return -2;
1397         if (*listing) return -2;
1398
1399         aaa = (char *)malloc(strlen(username) + 6);
1400         if (!aaa) return -1;
1401
1402         sprintf(aaa, "RBIO %s", username);
1403         ret = CtdlIPCGenericCommand(aaa, NULL, 0, listing, &bytes, cret);
1404         free(aaa);
1405         return ret;
1406 }
1407
1408
1409 /* LBIO */
1410 int CtdlIPCListUsersWithBios(char **listing, char *cret)
1411 {
1412         size_t bytes;
1413
1414         if (!cret) return -2;
1415         if (!listing) return -2;
1416         if (*listing) return -2;
1417
1418         return CtdlIPCGenericCommand("LBIO", NULL, 0, listing, &bytes, cret);
1419 }
1420
1421
1422 /* STEL */
1423 int CtdlIPCStealthMode(int mode, char *cret)
1424 {
1425         char aaa[16];
1426
1427         if (!cret) return -1;
1428
1429         sprintf(aaa, "STEL %d", mode ? 1 : 0);
1430         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1431 }
1432
1433
1434 /* TERM */
1435 int CtdlIPCTerminateSession(int sid, char *cret)
1436 {
1437         char aaa[16];
1438
1439         if (!cret) return -1;
1440
1441         sprintf(aaa, "TERM %d", sid);
1442         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1443 }
1444
1445
1446 /* DOWN */
1447 int CtdlIPCTerminateServerNow(char *cret)
1448 {
1449         if (!cret) return -1;
1450
1451         return CtdlIPCGenericCommand("DOWN", NULL, 0, NULL, NULL, cret);
1452 }
1453
1454
1455 /* SCDN */
1456 int CtdlIPCTerminateServerScheduled(int mode, char *cret)
1457 {
1458         char aaa[16];
1459
1460         if (!cret) return -1;
1461
1462         sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1463         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1464 }
1465
1466
1467 /* EMSG */
1468 int CtdlIPCEnterSystemMessage(const char *filename, const char *text,
1469                 char *cret)
1470 {
1471         register int ret;
1472         char *aaa;
1473
1474         if (!cret) return -2;
1475         if (!text) return -2;
1476         if (!filename) return -2;
1477
1478         aaa = (char *)malloc(strlen(filename) + 6);
1479         if (!aaa) return -1;
1480
1481         sprintf(aaa, "EMSG %s", filename);
1482         ret = CtdlIPCGenericCommand(aaa, text, strlen(text), NULL, NULL, cret);
1483         free(aaa);
1484         return ret;
1485 }
1486
1487
1488 /* HCHG */
1489 int CtdlIPCChangeHostname(const char *hostname, char *cret)
1490 {
1491         register int ret;
1492         char *aaa;
1493
1494         if (!cret) return -2;
1495         if (!hostname) return -2;
1496
1497         aaa = (char *)malloc(strlen(hostname) + 6);
1498         if (!aaa) return -1;
1499
1500         sprintf(aaa, "HCHG %s", hostname);
1501         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1502         free(aaa);
1503         return ret;
1504 }
1505
1506
1507 /* RCHG */
1508 int CtdlIPCChangeRoomname(const char *roomname, char *cret)
1509 {
1510         register int ret;
1511         char *aaa;
1512
1513         if (!cret) return -2;
1514         if (!roomname) return -2;
1515
1516         aaa = (char *)malloc(strlen(roomname) + 6);
1517         if (!aaa) return -1;
1518
1519         sprintf(aaa, "RCHG %s", roomname);
1520         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1521         free(aaa);
1522         return ret;
1523 }
1524
1525
1526 /* UCHG */
1527 int CtdlIPCChangeUsername(const char *username, char *cret)
1528 {
1529         register int ret;
1530         char *aaa;
1531
1532         if (!cret) return -2;
1533         if (!username) return -2;
1534
1535         aaa = (char *)malloc(strlen(username) + 6);
1536         if (!aaa) return -1;
1537
1538         sprintf(aaa, "UCHG %s", username);
1539         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1540         free(aaa);
1541         return ret;
1542 }
1543
1544
1545 /* TIME */
1546 /* This function returns the actual server time reported, or 0 if error */
1547 time_t CtdlIPCServerTime(char *cret)
1548 {
1549         register time_t tret;
1550         register int ret;
1551
1552         ret = CtdlIPCGenericCommand("TIME", NULL, 0, NULL, NULL, cret);
1553         if (ret / 100 == 2) {
1554                 tret = extract_long(cret, 0);
1555         } else {
1556                 tret = 0L;
1557         }
1558         return tret;
1559 }
1560
1561
1562 /* AGUP */
1563 int CtdlIPCAideGetUserParameters(const char *who,
1564                                  struct usersupp **uret, char *cret)
1565 {
1566         register int ret;
1567         char aaa[SIZ];
1568
1569         if (!cret) return -2;
1570         if (!uret) return -2;
1571         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof(struct usersupp));
1572         if (!*uret) return -1;
1573
1574         sprintf(aaa, "AGUP %s", who);
1575         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1576
1577         if (ret / 100 == 2) {
1578                 extract(uret[0]->fullname, cret, 0);
1579                 extract(uret[0]->password, cret, 1);
1580                 uret[0]->flags = extract_int(cret, 2);
1581                 uret[0]->timescalled = extract_long(cret, 3);
1582                 uret[0]->posted = extract_long(cret, 4);
1583                 uret[0]->axlevel = extract_int(cret, 5);
1584                 uret[0]->usernum = extract_long(cret, 6);
1585                 uret[0]->lastcall = extract_long(cret, 7);
1586                 uret[0]->USuserpurge = extract_int(cret, 8);
1587         }
1588         return ret;
1589 }
1590
1591
1592 /* ASUP */
1593 int CtdlIPCAideSetUserParameters(const struct usersupp *uret, char *cret)
1594 {
1595         register int ret;
1596         char *aaa;
1597
1598         if (!cret) return -2;
1599         if (!uret) return -2;
1600
1601         aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1602         if (!aaa) return -1;
1603
1604         sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1605                         uret->fullname, uret->password, uret->flags,
1606                         uret->timescalled, uret->posted, uret->axlevel,
1607                         uret->usernum, uret->lastcall, uret->USuserpurge);
1608         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1609         free(aaa);
1610         return ret;
1611 }
1612
1613
1614 /* GPEX */
1615 /* which is 0 = room, 1 = floor, 2 = site */
1616 int CtdlIPCGetMessageExpirationPolicy(int which, char *cret)
1617 {
1618         static char *proto[] = {"room", "floor", "site"};
1619         char aaa[11];
1620
1621         if (!cret) return -2;
1622         if (which < 0 || which > 2) return -2;
1623         
1624         sprintf(aaa, "GPEX %s", proto[which]);
1625         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1626 }
1627
1628
1629 /* SPEX */
1630 /* which is 0 = room, 1 = floor, 2 = site */
1631 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1632 int CtdlIPCSetMessageExpirationPolicy(int which, int policy, int value,
1633                 char *cret)
1634 {
1635         char aaa[38];
1636
1637         if (!cret) return -2;
1638         if (which < 0 || which > 2) return -2;
1639         if (policy < 0 || policy > 3) return -2;
1640         if (policy >= 2 && value < 1) return -2;
1641
1642         sprintf(aaa, "SPEX %d|%d|%d", which, policy, value);
1643         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1644 }
1645
1646
1647 /* CONF GET */
1648 int CtdlGetSystemConfig(char **listing, char *cret)
1649 {
1650         size_t bytes;
1651
1652         if (!cret) return -2;
1653         if (!listing) return -2;
1654         if (*listing) return -2;
1655
1656         return CtdlIPCGenericCommand("CONF GET", NULL, 0,
1657                         listing, &bytes, cret);
1658 }
1659
1660
1661 /* CONF SET */
1662 int CtdlSetSystemConfig(const char *listing, char *cret)
1663 {
1664         if (!cret) return -2;
1665         if (!listing) return -2;
1666
1667         return CtdlIPCGenericCommand("CONF SET", listing, strlen(listing),
1668                         NULL, NULL, cret);
1669 }
1670
1671
1672 /* MMOD */
1673 int CtdlIPCModerateMessage(long msgnum, int level, char *cret)
1674 {
1675         char aaa[27];
1676
1677         if (!cret) return -2;
1678         if (!msgnum) return -2;
1679
1680         sprintf(aaa, "MMOD %ld|%d", msgnum, level);
1681         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1682 }
1683
1684
1685 /* REQT */
1686 int CtdlIPCRequestClientLogout(int session, char *cret)
1687 {
1688         char aaa[16];
1689
1690         if (!cret) return -2;
1691         if (session < 0) return -2;
1692
1693         sprintf(aaa, "REQT %d", session);
1694         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1695 }
1696
1697
1698 /* SEEN */
1699 int CtdlIPCSetMessageSeen(long msgnum, int seen, char *cret)
1700 {
1701         char aaa[27];
1702
1703         if (!cret) return -2;
1704         if (msgnum < 0) return -2;
1705
1706         sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1707         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1708 }
1709
1710
1711 /* STLS */
1712 int CtdlIPCStartEncryption(char *cret)
1713 {
1714         return CtdlIPCGenericCommand("STLS", NULL, 0, NULL, NULL, cret);
1715 }
1716
1717
1718 /* QDIR */
1719 int CtdlIPCDirectoryLookup(const char *address, char *cret)
1720 {
1721         char *aaa;
1722
1723         if (!address) return -2;
1724         if (!cret) return -2;
1725
1726         aaa = (char *)malloc(strlen(address) + 6);
1727         if (!aaa) return -1;
1728
1729         sprintf(aaa, "QDIR %s", address);
1730         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1731 }
1732
1733
1734 /*
1735  * Not implemented:
1736  * 
1737  * CHAT
1738  * ETLS
1739  * EXPI
1740  * GTLS
1741  * IGAB
1742  * IPGM
1743  * MSG3
1744  * MSG4
1745  * NDOP
1746  * NETP
1747  * NUOP
1748  * SMTP
1749  */
1750
1751
1752 /* ************************************************************************** */
1753 /*             Stuff below this line is not for public consumption            */
1754 /* ************************************************************************** */
1755
1756
1757 inline void netio_lock(void)
1758 {
1759 #ifdef THREADED_CLIENT
1760         pthread_mutex_lock(&rwlock);
1761 #endif
1762 }
1763
1764
1765 inline void netio_unlock(void)
1766 {
1767 #ifdef THREADED_CLIENT
1768         pthread_mutex_unlock(&rwlock);
1769 #endif
1770 }
1771
1772
1773 /* Read a listing from the server up to 000.  Append to dest if it exists */
1774 char *CtdlIPCReadListing(char *dest)
1775 {
1776         size_t length = 0;
1777         size_t linelength;
1778         char *ret;
1779         char aaa[SIZ];
1780
1781         ret = dest;
1782         if (ret != NULL) {
1783                 length = strlen(ret);
1784         }
1785         else {
1786                 ret = strdup("");
1787                 length = 0;
1788         }
1789
1790         while (serv_gets(aaa), strcmp(aaa, "000")) {
1791                 linelength = strlen(aaa);
1792                 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
1793                 if (ret) {
1794                         strcpy(&ret[length], aaa);
1795                         length += linelength;
1796                         strcpy(&ret[length++], "\n");
1797                 }
1798         }
1799
1800         return(ret);
1801 }
1802
1803
1804 /* Send a listing to the server; generate the ending 000. */
1805 int CtdlIPCSendListing(const char *listing)
1806 {
1807         char *text;
1808
1809         text = (char *)malloc(strlen(listing) + 6);
1810         if (text) {
1811                 strcpy(text, listing);
1812                 while (text[strlen(text) - 1] == '\n')
1813                         text[strlen(text) - 1] = '\0';
1814                 strcat(text, "\n000");
1815                 serv_puts(text);
1816                 free(text);
1817                 text = NULL;
1818         } else {
1819                 /* Malloc failed but we are committed to send */
1820                 /* This may result in extra blanks at the bottom */
1821                 serv_puts(text);
1822                 serv_puts("000");
1823         }
1824         return 0;
1825 }
1826
1827
1828 /* Partial read of file from server */
1829 size_t CtdlIPCPartialRead(void **buf, size_t offset, size_t bytes, char *cret)
1830 {
1831         register size_t len = 0;
1832         char aaa[SIZ];
1833
1834         if (!buf) return -1;
1835         if (!cret) return -1;
1836         if (bytes < 1) return -1;
1837         if (offset < 0) return -1;
1838
1839         netio_lock();
1840         sprintf(aaa, "READ %d|%d", offset, bytes);
1841         serv_puts(aaa);
1842         serv_gets(aaa);
1843         if (aaa[0] != '6')
1844                 strcpy(cret, &aaa[4]);
1845         else {
1846                 len = extract_long(&aaa[4], 0);
1847                 *buf = (void *)realloc(*buf, (size_t)(offset + len));
1848                 if (*buf) {
1849                         /* I know what I'm doing */
1850                         serv_read((char *)&buf[offset], len);
1851                 } else {
1852                         /* We have to read regardless */
1853                         serv_read(aaa, len);
1854                         len = -1;
1855                 }
1856         }
1857         netio_unlock();
1858         return len;
1859 }
1860
1861
1862 /* CLOS */
1863 int CtdlIPCEndDownload(char *cret)
1864 {
1865         register int ret;
1866
1867         if (!cret) return -2;
1868         if (!download_in_progress) return -2;
1869
1870         ret = CtdlIPCGenericCommand("CLOS", NULL, 0, NULL, NULL, cret);
1871         if (ret / 100 == 2)
1872                 download_in_progress = 0;
1873         return ret;
1874 }
1875
1876
1877 /* MSGP */
1878 int CtdlIPCSpecifyPreferredFormats(char *cret, char *formats) {
1879         register int ret;
1880         char cmd[SIZ];
1881         
1882         snprintf(cmd, sizeof cmd, "MSGP %s", formats);
1883         ret = CtdlIPCGenericCommand(cmd, NULL, 0, NULL, NULL, cret);
1884         return ret;
1885 }
1886
1887
1888
1889 /* READ */
1890 int CtdlIPCReadDownload(void **buf, size_t bytes, char *cret)
1891 {
1892         register size_t len;
1893
1894         if (!cret) return -1;
1895         if (!buf) return -1;
1896         if (*buf) return -1;
1897         if (!download_in_progress) return -1;
1898
1899         len = 0;
1900         while (len < bytes) {
1901                 len = CtdlIPCPartialRead(buf, len, 4096, cret);
1902                 if (len == -1) {
1903                         free(*buf);
1904                         return 0;
1905                 }
1906         }
1907         return len;
1908 }
1909
1910
1911 /* UCLS */
1912 int CtdlIPCEndUpload(char *cret)
1913 {
1914         register int ret;
1915
1916         if (!cret) return -1;
1917         if (!upload_in_progress) return -1;
1918
1919         ret = CtdlIPCGenericCommand("UCLS", NULL, 0, NULL, NULL, cret);
1920         if (ret / 100 == 2)
1921                 upload_in_progress = 0;
1922         return ret;
1923 }
1924
1925
1926 /* WRIT */
1927 int CtdlIPCWriteUpload(void *buf, size_t bytes, char *cret)
1928 {
1929         register int ret = -1;
1930         register size_t offset;
1931         char aaa[SIZ];
1932
1933         if (!cret) return -1;
1934         if (!buf) return -1;
1935         if (bytes < 1) return -1;
1936
1937         offset = 0;
1938         while (offset < bytes) {
1939                 sprintf(aaa, "WRIT %d", bytes - offset);
1940                 serv_puts(aaa);
1941                 serv_gets(aaa);
1942                 strcpy(cret, &aaa[4]);
1943                 ret = atoi(aaa);
1944                 if (aaa[0] == '7') {
1945                         register size_t to_write;
1946
1947                         to_write = extract_long(&aaa[4], 0);
1948                         serv_write(buf + offset, to_write);
1949                         offset += to_write;
1950                 } else {
1951                         break;
1952                 }
1953         }
1954         return ret;
1955 }
1956
1957
1958 /*
1959  * Generic command method.  This method should handle any server command
1960  * except for CHAT.  It takes the following arguments:
1961  *
1962  * command              Preformatted command to send to server
1963  * to_send              A text or binary file to send to server
1964  *                      (only sent if server requests it)
1965  * bytes_to_send        The number of bytes in to_send (required if
1966  *                      sending binary, optional if sending listing)
1967  * to_receive           Pointer to a NULL pointer, if the server
1968  *                      sends text or binary we will allocate memory
1969  *                      for the file and stuff it here
1970  * bytes_to_receive     If a file is received, we will store its
1971  *                      byte count here
1972  * proto_response       The protocol response.  Caller must provide
1973  *                      this buffer and ensure that it is at least
1974  *                      128 bytes in length.
1975  *
1976  * This function returns a number equal to the protocol response number,
1977  * -1 if an internal error occurred, -2 if caller provided bad values,
1978  * or 0 - the protocol response number if bad values were found during
1979  * the protocol exchange.
1980  * It stores the protocol response string (minus the number) in 
1981  * protocol_response as described above.  Some commands send additional
1982  * data in this string.
1983  */
1984 int CtdlIPCGenericCommand(const char *command, const char *to_send,
1985                 size_t bytes_to_send, char **to_receive, 
1986                 size_t *bytes_to_receive, char *proto_response)
1987 {
1988         char buf[SIZ];
1989         register int ret;
1990
1991         if (!command) return -2;
1992         if (!proto_response) return -2;
1993
1994         netio_lock();
1995         serv_puts((char *)command);
1996         while (1) {
1997                 serv_gets(proto_response);
1998                 if (proto_response[3] == '*')
1999                         express_msgs = 1;
2000                 ret = atoi(proto_response);
2001                 strcpy(proto_response, &proto_response[4]);
2002                 switch (ret / 100) {
2003                 default:                        /* Unknown, punt */
2004                 case 2:                         /* OK */
2005                 case 3:                         /* MORE_DATA */
2006                 case 5:                         /* ERROR */
2007                         /* Don't need to do anything */
2008                         break;
2009                 case 1:                         /* LISTING_FOLLOWS */
2010                         if (to_receive && !*to_receive && bytes_to_receive) {
2011                                 *to_receive = CtdlIPCReadListing(NULL);
2012                         } else { /* Drain */
2013                                 while (serv_gets(buf), strcmp(buf, "000")) ;
2014                                 ret = -ret;
2015                         }
2016                         break;
2017                 case 4:                         /* SEND_LISTING */
2018                         if (to_send) {
2019                                 CtdlIPCSendListing(to_send);
2020                         } else {
2021                                 /* No listing given, fake it */
2022                                 serv_puts("000");
2023                                 ret = -ret;
2024                         }
2025                         break;
2026                 case 6:                         /* BINARY_FOLLOWS */
2027                         if (to_receive && !*to_receive && bytes_to_receive) {
2028                                 *bytes_to_receive =
2029                                         extract_long(proto_response, 0);
2030                                 *to_receive = (char *)
2031                                         malloc((size_t)*bytes_to_receive);
2032                                 if (!*to_receive) {
2033                                         ret = -1;
2034                                 } else {
2035                                         serv_read(*to_receive,
2036                                                         *bytes_to_receive);
2037                                 }
2038                         } else {
2039                                 /* Drain */
2040                                 size_t drain;
2041
2042                                 drain = extract_long(proto_response, 0);
2043                                 while (drain > SIZ) {
2044                                         serv_read(buf, SIZ);
2045                                         drain -= SIZ;
2046                                 }
2047                                 serv_read(buf, drain);
2048                                 ret = -ret;
2049                         }
2050                         break;
2051                 case 7:                         /* SEND_BINARY */
2052                         if (to_send && bytes_to_send) {
2053                                 serv_write((char *)to_send, bytes_to_send);
2054                         } else if (bytes_to_send) {
2055                                 /* Fake it, send nulls */
2056                                 size_t fake;
2057
2058                                 fake = bytes_to_send;
2059                                 memset(buf, '\0', SIZ);
2060                                 while (fake > SIZ) {
2061                                         serv_write(buf, SIZ);
2062                                         fake -= SIZ;
2063                                 }
2064                                 serv_write(buf, fake);
2065                                 ret = -ret;
2066                         } /* else who knows?  DANGER WILL ROBINSON */
2067                         break;
2068                 case 8:                         /* START_CHAT_MODE */
2069                         if (!strncasecmp(command, "CHAT", 4)) {
2070                                 /* Don't call chatmode with generic! */
2071                                 serv_puts("/quit");
2072                                 ret = -ret;
2073                         } else {
2074                                 /* In this mode we send then receive listing */
2075                                 if (to_send) {
2076                                         CtdlIPCSendListing(to_send);
2077                                 } else {
2078                                         /* No listing given, fake it */
2079                                         serv_puts("000");
2080                                         ret = -ret;
2081                                 }
2082                                 if (to_receive && !*to_receive
2083                                                 && bytes_to_receive) {
2084                                         *to_receive = CtdlIPCReadListing(NULL);
2085                                 } else { /* Drain */
2086                                         while (serv_gets(buf),
2087                                                         strcmp(buf, "000")) ;
2088                                         ret = -ret;
2089                                 }
2090                         }
2091                         break;
2092                 case 9:                         /* ASYNC_MSG */
2093                         /* CtdlIPCDoAsync(ret, proto_response); */
2094                         free(CtdlIPCReadListing(NULL)); /* STUB FIXME */
2095                         break;
2096                 }
2097                 if (ret / 100 != 9)
2098                         break;
2099         }
2100         netio_unlock();
2101         return ret;
2102 }