Tizen 2.1 base
[platform/upstream/hplip.git] / prnt / hpijs / ijs_server.c
1 /**
2  * Copyright (c) 2001-2004 artofcode LLC.
3  *
4  * Permission is hereby granted, free of charge, to any person
5  * obtaining a copy of this software and associated documentation
6  * files (the "Software"), to deal in the Software without
7  * restriction, including without limitation the rights to use, copy,
8  * modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23 **/
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include "unistd_.h"
28 #include <string.h>
29
30 #include "ijs.h"
31 #include "ijs_server.h"
32
33 #define noVERBOSE
34
35 typedef enum {
36   IJS_N_CHAN_SET = 1,
37   IJS_BPS_SET = 2,
38   IJS_CS_SET = 4,
39   IJS_WIDTH_SET = 8,
40   IJS_HEIGHT_SET = 16,
41   IJS_DPI_SET = 32
42 } IjsFieldsSet;
43
44 #define IJS_FIELDS_REQUIRED (IJS_N_CHAN_SET | IJS_BPS_SET | IJS_CS_SET | \
45   IJS_WIDTH_SET | IJS_HEIGHT_SET | IJS_DPI_SET)
46
47 struct _IjsServerCtx {
48   int fd_from;
49   int child_pid;
50   IjsSendChan send_chan;
51   IjsRecvChan recv_chan;
52   int version;
53
54   /* callbacks */
55   IjsBeginJobCb *begin_cb;
56   void *begin_cb_data;
57   IjsEndJobCb *end_cb;
58   void *end_cb_data;
59   IjsQueryStatusCb *status_cb;
60   void *status_cb_data;
61   IjsListParamsCb *list_cb;
62   void *list_cb_data;
63   IjsEnumParamCb *enum_cb;
64   void *enum_cb_data;
65   IjsSetParamCb *set_cb;
66   void *set_cb_data;
67   IjsGetParamCb *get_cb;
68   void *get_cb_data;
69
70   ijs_bool in_job;
71   IjsJobId job_id;
72
73   IjsPageHeader *ph;
74
75   /* This should be IjsFieldsSet, but David Suffield reports that this
76      causes problems when compiling with g++. */
77   int fields_set;
78   ijs_bool in_page;
79
80   char *buf;
81   int buf_size;
82   int buf_ix;
83   char *overflow_buf;
84   int overflow_buf_size;
85   int overflow_buf_ix;
86 };
87
88 static int
89 ijs_server_dummy_begin_cb (void *begin_cb_data,
90                            IjsServerCtx *ctx,
91                            IjsJobId job_id)
92 {
93   return 0;
94 }
95
96 static int
97 ijs_server_dummy_end_cb (void *end_cb_data,
98                          IjsServerCtx *ctx,
99                          IjsJobId job_id)
100 {
101   return 0;
102 }
103
104 IjsServerCtx *
105 ijs_server_init (void)
106 {
107   ijs_bool ok = TRUE;
108   char helo_buf[8];
109   char resp_buf[8];
110   int nbytes;
111   IjsServerCtx *ctx = (IjsServerCtx *)malloc (sizeof(IjsServerCtx));
112   int fd_from, fd_to;
113
114   memcpy (resp_buf, IJS_RESP_STR, sizeof(resp_buf));
115
116   fd_from = 0;
117   fd_to = 1;
118 #ifdef _MSC_VER
119   _setmode(fd_from, _O_BINARY);
120   _setmode(fd_to, _O_BINARY);
121 #endif
122 #ifdef VERBOSE
123   fprintf (stderr, "fd_from = %d, fd_to = %d\n", fd_from, fd_to);
124 #endif
125   ijs_recv_init (&ctx->recv_chan, fd_from);
126   ijs_send_init (&ctx->send_chan, fd_to);
127   nbytes = read (ctx->recv_chan.fd, helo_buf, sizeof(helo_buf));
128
129   if (nbytes != sizeof(helo_buf))
130     ok = FALSE;
131
132   if (ok)
133     nbytes = write (ctx->send_chan.fd, resp_buf, sizeof(resp_buf));
134   if (nbytes != sizeof(resp_buf))
135     ok = FALSE;
136
137   ctx->in_job = FALSE;
138   ctx->job_id = -1;
139
140   ctx->ph = NULL;
141   ctx->in_page = FALSE;
142   ctx->buf = NULL;
143   ctx->overflow_buf = NULL;
144
145   ctx->begin_cb = ijs_server_dummy_begin_cb;
146   ctx->end_cb = ijs_server_dummy_end_cb;
147
148   if (ok)
149     return ctx;
150   else
151     {
152       ijs_server_done (ctx);
153       return NULL;
154     }
155 }
156
157 int
158 ijs_server_install_begin_cb (IjsServerCtx *ctx,
159                              IjsBeginJobCb *begin_cb, void *begin_cb_data)
160 {
161   ctx->begin_cb = begin_cb;
162   ctx->begin_cb_data = begin_cb_data;
163   return 0;
164 }
165
166 int
167 ijs_server_install_end_cb (IjsServerCtx *ctx,
168                            IjsEndJobCb *end_cb, void *end_cb_data)
169 {
170   ctx->end_cb = end_cb;
171   ctx->end_cb_data = end_cb_data;
172   return 0;
173 }
174
175 int
176 ijs_server_install_status_cb (IjsServerCtx *ctx,
177                               IjsQueryStatusCb *status_cb,
178                               void *status_cb_data)
179 {
180   ctx->status_cb = status_cb;
181   ctx->status_cb_data = status_cb_data;
182   return 0;
183 }
184
185 int
186 ijs_server_install_list_cb (IjsServerCtx *ctx,
187                            IjsListParamsCb *list_cb, void *list_cb_data)
188 {
189   ctx->list_cb = list_cb;
190   ctx->list_cb_data = list_cb_data;
191   return 0;
192 }
193
194 int
195 ijs_server_install_enum_cb (IjsServerCtx *ctx,
196                            IjsEnumParamCb *enum_cb, void *enum_cb_data)
197 {
198   ctx->enum_cb = enum_cb;
199   ctx->enum_cb_data = enum_cb_data;
200   return 0;
201 }
202
203 int
204 ijs_server_install_set_cb (IjsServerCtx *ctx,
205                            IjsSetParamCb *set_cb, void *set_cb_data)
206 {
207   ctx->set_cb = set_cb;
208   ctx->set_cb_data = set_cb_data;
209   return 0;
210 }
211
212 int
213 ijs_server_install_get_cb (IjsServerCtx *ctx,
214                            IjsGetParamCb *get_cb, void *get_cb_data)
215 {
216   ctx->get_cb = get_cb;
217   ctx->get_cb_data = get_cb_data;
218   return 0;
219 }
220
221 static int
222 ijs_server_ack (IjsServerCtx *ctx)
223 {
224   int status;
225
226   status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
227   if (status < 0)
228     return status;
229   return ijs_send_buf (&ctx->send_chan);
230 }
231
232 void
233 ijs_server_done (IjsServerCtx *ctx)
234 {
235   /* todo: close channels */
236   ijs_server_ack (ctx);
237
238   free (ctx);
239 }
240
241 static int
242 ijs_server_nak (IjsServerCtx *ctx, int errorcode)
243 {
244   int status;
245
246   status = ijs_send_begin (&ctx->send_chan, IJS_CMD_NAK);
247   if (status < 0)
248     return status;
249   status = ijs_send_int (&ctx->send_chan, errorcode);
250   if (status < 0)
251     return status;
252   return ijs_send_buf (&ctx->send_chan);
253 }
254
255 /* The return code is: 0 if ok, positive on normal exit, negative on error */
256 typedef int (*ijs_server_proc) (IjsServerCtx *ctx);
257
258 static int
259 ijs_server_proc_ack (IjsServerCtx *ctx)
260 {
261   /* servers should not get ack commands */
262   return IJS_EPROTO;
263 }
264
265 static int
266 ijs_server_proc_nak (IjsServerCtx *ctx)
267 {
268   /* servers should not get nak commands */
269   return IJS_EPROTO;
270 }
271
272 static int
273 ijs_server_proc_ping (IjsServerCtx *ctx)
274 {
275   int status;
276   int version;
277
278   status = ijs_recv_int (&ctx->recv_chan, &version);
279   if (status < 0)
280     return status;
281   if (version > IJS_VERSION)
282     version = IJS_VERSION;
283   ctx->version = version;
284
285 #ifdef VERBOSE
286   fprintf (stderr, "ping version=%d\n", version);
287 #endif
288   status = ijs_send_begin (&ctx->send_chan, IJS_CMD_PONG);
289   if (status < 0)
290     return status;
291   status = ijs_send_int (&ctx->send_chan, IJS_VERSION);
292   if (status < 0)
293     return status;
294   return ijs_send_buf (&ctx->send_chan);
295 }
296
297 static int
298 ijs_server_proc_pong (IjsServerCtx *ctx)
299 {
300   /* servers should not get pong commands */
301   return IJS_EPROTO;
302 }
303
304 static int
305 ijs_server_proc_open (IjsServerCtx *ctx)
306 {
307   /* A server might allocate tables here. */
308   return ijs_server_ack (ctx);
309 }
310
311 static int
312 ijs_server_proc_close (IjsServerCtx *ctx)
313 {
314   /* A server might deallocate memory here. */
315   return ijs_server_ack (ctx);
316 }
317
318 static int
319 ijs_server_proc_begin_job (IjsServerCtx *ctx)
320 {
321   int code;
322   IjsJobId job_id;
323
324   code = ijs_recv_int (&ctx->recv_chan, &job_id);
325   if (code < 0) return code;
326
327   if (ctx->in_job)
328     return ijs_server_nak (ctx, IJS_ETOOMANYJOBS);
329   ctx->in_job = TRUE;
330   ctx->job_id = job_id;
331
332   return ijs_server_ack (ctx);
333 }
334
335 static int
336 ijs_server_proc_end_job (IjsServerCtx *ctx)
337 {
338   int code;
339   IjsJobId job_id;
340
341   code = ijs_recv_int (&ctx->recv_chan, &job_id);
342   if (code < 0) return code;
343
344   if (!ctx->in_job || job_id != ctx->job_id)
345     return ijs_server_nak (ctx, IJS_EJOBID);
346   ctx->in_job = FALSE;
347
348   return ijs_server_ack (ctx);
349 }
350
351 static int
352 ijs_server_proc_cancel_job (IjsServerCtx *ctx)
353 {
354   int code;
355   IjsJobId job_id;
356
357   code = ijs_recv_int (&ctx->recv_chan, &job_id);
358   if (code < 0) return code;
359
360   if (!ctx->in_job || job_id != ctx->job_id)
361     return ijs_server_nak (ctx, IJS_EJOBID);
362   /* todo: call cancel callback here */
363   ctx->in_job = FALSE;
364
365   return ijs_server_ack (ctx);
366 }
367
368 static int
369 ijs_server_proc_query_status (IjsServerCtx *ctx)
370 {
371   int code;
372   IjsJobId job_id;
373
374   code = ijs_recv_int (&ctx->recv_chan, &job_id);
375   if (code < 0)
376     return code;
377
378   if (!ctx->in_job || ctx->job_id != job_id)
379     return ijs_server_nak (ctx, IJS_EJOBID);
380
381   code = ctx->status_cb (ctx->list_cb_data, ctx, job_id);
382   if (code < 0)
383     return ijs_server_nak (ctx, code);
384   else
385     {
386       int status;
387
388       status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
389       if (status < 0)
390         return status;
391       status = ijs_send_int (&ctx->send_chan, code);
392       if (status < 0)
393         return status;
394       return ijs_send_buf (&ctx->send_chan);
395     }
396 }
397
398 static int
399 ijs_server_proc_list_params (IjsServerCtx *ctx)
400 {
401   int code;
402   char buf[4096];
403   IjsJobId job_id;
404
405   code = ijs_recv_int (&ctx->recv_chan, &job_id);
406   if (code < 0)
407     return code;
408
409   if (!ctx->in_job || ctx->job_id != job_id)
410     return ijs_server_nak (ctx, IJS_EJOBID);
411
412   code = ctx->list_cb (ctx->list_cb_data, ctx, job_id, buf, sizeof(buf));
413   if (code < 0)
414     return ijs_server_nak (ctx, code);
415   else
416     {
417       int status;
418
419       status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
420       if (status < 0)
421         return status;
422       status = ijs_send_block (&ctx->send_chan, buf, code);
423       if (status < 0)
424         return status;
425       return ijs_send_buf (&ctx->send_chan);
426     }
427 }
428
429 static int
430 ijs_server_proc_enum_param (IjsServerCtx *ctx)
431 {
432   const char *key;
433   int key_size;
434   int code;
435   char buf[4096];
436   IjsJobId job_id;
437
438   code = ijs_recv_int (&ctx->recv_chan, &job_id);
439   if (code < 0)
440     return code;
441
442   if (!ctx->in_job || ctx->job_id != job_id)
443     return ijs_server_nak (ctx, IJS_EJOBID);
444
445   key = ctx->recv_chan.buf + ctx->recv_chan.buf_idx;
446   key_size = ctx->recv_chan.buf_size - ctx->recv_chan.buf_idx;
447   if (key_size == 0 || key[key_size - 1])
448     return IJS_ESYNTAX;
449 #ifdef VERBOSE
450   fprintf (stderr, "ijs_server_proc_enum_param, key_size = %d\n", key_size);
451 #endif
452
453   code = ctx->enum_cb (ctx->enum_cb_data, ctx, job_id, key, buf, sizeof(buf));
454   if (code < 0)
455     return ijs_server_nak (ctx, code);
456   else
457     {
458       int status;
459
460       status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
461       if (status < 0)
462         return status;
463       status = ijs_send_block (&ctx->send_chan, buf, code);
464       if (status < 0)
465         return status;
466       return ijs_send_buf (&ctx->send_chan);
467     }
468 }
469
470 static int
471 ijs_strnlen (const char *s, int size)
472 {
473   int i;
474
475   for (i = 0; i < size; i++)
476     if (s[i] == 0)
477       return i;
478   return size;
479 }
480
481 static int
482 ijs_server_parse_int (const char *value, int size, int *result)
483 {
484   int num = 0;
485   int i;
486   int sign = 1;
487
488   i = 0;
489   if (i == size)
490     return IJS_ESYNTAX;
491   if (value[i] == '-')
492     {
493       sign = -1;
494       i++;
495     }
496
497   if (i == size)
498     return IJS_ESYNTAX;
499
500   for (; i < size; i++)
501     {
502       char c = value[i];
503       if (c < '0' || c > '9')
504         return IJS_ESYNTAX;
505       num = (num * 10) + (c - '0');
506     }
507   *result = num;
508   return 0;
509 }
510
511 static int
512 ijs_server_parse_float (const char *value, int size, double *result)
513 {
514   char buf[256];
515   char *tail;
516
517   if (size + 1 > sizeof(buf))
518     return IJS_EBUF;
519   memcpy (buf, value, size);
520   buf[size] = 0;
521   *result = strtod (buf, &tail);
522   if (tail == buf)
523     return IJS_ESYNTAX;
524   return 0;
525 }
526
527 static int
528 ijs_server_set_param (IjsServerCtx *ctx, IjsJobId job_id, const char *key,
529                       const char *value, int value_size)
530 {
531   int code;
532
533 #ifdef VERBOSE
534   fprintf (stderr, "set_param %s = ", key);
535   fwrite (value, 1, value_size, stderr);
536   fputs ("\n", stderr);
537 #endif
538   if (!strcmp (key, "NumChan"))
539     {
540       code = ijs_server_parse_int (value, value_size, &ctx->ph->n_chan);
541       if (code == 0)
542         ctx->fields_set |= IJS_N_CHAN_SET;
543       return code;
544     }
545   else if (!strcmp (key, "BitsPerSample"))
546     {
547       code = ijs_server_parse_int (value, value_size, &ctx->ph->bps);
548       if (code == 0)
549         ctx->fields_set |= IJS_BPS_SET;
550       return code;
551     }
552   else if (!strcmp (key, "ColorSpace"))
553     {
554       int size = value_size;
555
556       if (size > (int)sizeof(ctx->ph->cs) - 1)
557         size = sizeof(ctx->ph->cs) - 1;
558       memcpy (ctx->ph->cs, value, size);
559       ctx->ph->cs[size] = 0;
560         ctx->fields_set |= IJS_CS_SET;
561       return 0;
562     }
563   else if (!strcmp (key, "Width"))
564     {
565       code = ijs_server_parse_int (value, value_size, &ctx->ph->width);
566       if (code == 0)
567         ctx->fields_set |= IJS_WIDTH_SET;
568       return code;
569     }
570   else if (!strcmp (key, "Height"))
571     {
572       code = ijs_server_parse_int (value, value_size, &ctx->ph->height);
573       if (code == 0)
574         ctx->fields_set |= IJS_HEIGHT_SET;
575       return code;
576     }
577   else if (!strcmp (key, "Dpi"))
578     {
579       int x_ix;
580
581       for (x_ix = 0; x_ix < value_size; x_ix++)
582         if (value[x_ix] == 'x')
583           break;
584       if (x_ix == value_size)
585         return IJS_ESYNTAX;
586       code = ijs_server_parse_float (value, x_ix, &ctx->ph->xres);
587       if (code < 0)
588         return code;
589       code = ijs_server_parse_float (value + x_ix + 1, value_size - (x_ix + 1),
590                                      &ctx->ph->yres);
591       if (code < 0)
592         return code;
593       ctx->fields_set |= IJS_DPI_SET;
594       return 0;
595     }
596   else
597     {
598       return ctx->set_cb (ctx->set_cb_data, ctx, job_id, key, value, value_size);
599     }
600 }
601
602 static int
603 ijs_server_proc_set_param (IjsServerCtx *ctx)
604 {
605   const char *key, *value;
606   int key_size, value_size;
607   IjsJobId job_id;
608   int param_size;
609   int code;
610
611   code = ijs_recv_int (&ctx->recv_chan, &job_id);
612   if (code < 0)
613     return code;
614
615   if (!ctx->in_job || ctx->job_id != job_id)
616     return ijs_server_nak (ctx, IJS_EJOBID);
617
618   code = ijs_recv_int (&ctx->recv_chan, &param_size);
619   if (code < 0)
620     return code;
621   if (param_size != ctx->recv_chan.buf_size - ctx->recv_chan.buf_idx)
622       return IJS_EPROTO;
623   key = ctx->recv_chan.buf + ctx->recv_chan.buf_idx;
624   key_size = ijs_strnlen (key, ctx->recv_chan.buf_size);
625   if (key_size == param_size)
626     return IJS_EPROTO;
627   value = key + key_size + 1;
628   value_size = param_size - (key_size + 1);
629   code = ijs_server_set_param (ctx, job_id, key, value, value_size);
630   if (code)
631     return ijs_server_nak (ctx, code);
632   else
633     return ijs_server_ack (ctx);
634 }
635
636 static int
637 ijs_server_get_param (IjsServerCtx *ctx, IjsJobId job_id, const char *key,
638                       char *value, int value_size)
639 {
640 #ifdef VERBOSE
641   fprintf (stderr, "ijs_server_get_param %s\n", key);
642 #endif
643   return ctx->get_cb (ctx->get_cb_data, ctx, job_id, key,
644                       value, value_size);
645 }
646
647 static int
648 ijs_server_proc_get_param (IjsServerCtx *ctx)
649 {
650   const char *key;
651   int key_size;
652   int code;
653   char buf[4096];
654   IjsJobId job_id;
655
656   code = ijs_recv_int (&ctx->recv_chan, &job_id);
657   if (code < 0)
658     return code;
659
660   if (!ctx->in_job || ctx->job_id != job_id)
661     return ijs_server_nak (ctx, IJS_EJOBID);
662
663   key = ctx->recv_chan.buf + ctx->recv_chan.buf_idx;
664   key_size = ctx->recv_chan.buf_size - ctx->recv_chan.buf_idx;
665   if (key_size == 0 || key[key_size - 1])
666     return IJS_ESYNTAX;
667 #ifdef VERBOSE
668   fprintf (stderr, "ijs_server_proc_get_param, key_size = %d\n", key_size);
669 #endif
670
671   code = ijs_server_get_param (ctx, job_id, key, buf, sizeof(buf));
672   if (code < 0)
673     return ijs_server_nak (ctx, code);
674   else
675     {
676       int status;
677
678       status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
679       if (status < 0)
680         return status;
681       status = ijs_send_block (&ctx->send_chan, buf, code);
682       if (status < 0)
683         return status;
684       return ijs_send_buf (&ctx->send_chan);
685     }
686 }
687
688 static int
689 ijs_server_proc_begin_page (IjsServerCtx *ctx)
690 {
691   IjsPageHeader *ph = ctx->ph;
692   int status = 0;
693
694   if (ph == NULL)
695     status = IJS_EPROTO;
696   if ((ctx->fields_set & IJS_FIELDS_REQUIRED) != IJS_FIELDS_REQUIRED)
697     status = IJS_EPROTO;
698 #ifdef VERBOSE
699   fprintf (stderr, "begin page %d %d %d %d %d\n",
700            ph->n_chan, ph->bps, ph->cs, ph->width, ph->height);
701 #endif
702   if (!status)
703     {
704       ctx->in_page = TRUE;
705       return ijs_server_ack (ctx);
706     }
707   else
708     return ijs_server_nak (ctx, status);
709 }
710
711 static int
712 ijs_server_read_data (IjsServerCtx *ctx, char *buf, int size)
713 {
714   int status;
715
716   status = ijs_recv_read (&ctx->recv_chan, buf, size);
717   return (status == size) ? 0 : IJS_EIO;
718 }
719
720 static int
721 ijs_server_proc_send_data_block (IjsServerCtx *ctx)
722 {
723   int size;
724   int status = 0;
725   IjsJobId job_id;
726
727   status = ijs_recv_int (&ctx->recv_chan, &job_id);
728   if (status < 0) return status;
729
730   if (!ctx->in_job || job_id != ctx->job_id)
731     status = IJS_EJOBID;
732   else if (ctx->buf == NULL)
733     status = IJS_EPROTO;
734
735   if (!status) status = ijs_recv_int (&ctx->recv_chan, &size);
736
737 #ifdef VERBOSE
738   fprintf (stderr, "status=%d, send data block id=%d, size=%d\n",
739            status, job_id, size);
740 #endif
741   if (status)
742     return ijs_server_nak (ctx, status);
743
744   if (size <= ctx->buf_size - ctx->buf_ix)
745     {
746       status = ijs_server_read_data (ctx, ctx->buf + ctx->buf_ix, size);
747       ctx->buf_ix += size;
748     }
749   else
750     {
751       ctx->overflow_buf_size = size - (ctx->buf_size - ctx->buf_ix);
752       ctx->overflow_buf = (char *)malloc (ctx->overflow_buf_size);
753       ctx->overflow_buf_ix = 0;
754       status = ijs_server_read_data (ctx, ctx->buf + ctx->buf_ix,
755                                      ctx->buf_size - ctx->buf_ix);
756       ctx->buf_ix = ctx->buf_size;
757       if (!status)
758         {
759           status = ijs_server_read_data (ctx, ctx->overflow_buf,
760                                          ctx->overflow_buf_size);
761         }
762     }
763   return ijs_server_ack (ctx);
764 }
765
766 static int
767 ijs_server_proc_end_page (IjsServerCtx *ctx)
768 {
769 #ifdef VERBOSE
770   fprintf (stderr, "end page\n");
771 #endif
772   return ijs_server_ack (ctx);
773 }
774
775 static int
776 ijs_server_proc_exit (IjsServerCtx *ctx)
777 {
778   return 1;
779 }
780
781 ijs_server_proc ijs_server_procs[] = {
782   ijs_server_proc_ack,
783   ijs_server_proc_nak,
784   ijs_server_proc_ping,
785   ijs_server_proc_pong,
786   ijs_server_proc_open,
787   ijs_server_proc_close,
788   ijs_server_proc_begin_job,
789   ijs_server_proc_end_job,
790   ijs_server_proc_cancel_job,
791   ijs_server_proc_query_status,
792   ijs_server_proc_list_params,
793   ijs_server_proc_enum_param,
794   ijs_server_proc_set_param,
795   ijs_server_proc_get_param,
796   ijs_server_proc_begin_page,
797   ijs_server_proc_send_data_block,
798   ijs_server_proc_end_page,
799   ijs_server_proc_exit
800 };
801
802 int
803 ijs_server_iter (IjsServerCtx *ctx)
804 {
805   int cmd_num;
806   int status;
807
808   status = ijs_recv_buf (&ctx->recv_chan);
809
810   if (status < 0)
811     return status;
812
813   cmd_num = ijs_get_int (ctx->recv_chan.buf);
814 #ifdef VERBOSE
815   fprintf (stderr, "command %d, %d bytes\n", cmd_num, ctx->recv_chan.buf_size);
816 #endif
817   if (cmd_num < 0 ||
818       cmd_num >= (int)sizeof(ijs_server_procs) / sizeof(ijs_server_procs[0]))
819     return -1;
820   return ijs_server_procs[cmd_num] (ctx);
821 }
822
823 /**
824  * ijs_server_get_page_header: Get the page header.
825  * @ctx: The server context.
826  * @ph: Where to store the page header.
827  *
828  * Return value: 0 on success, 1 on normal exit, negative on error.
829  **/
830 int
831 ijs_server_get_page_header (IjsServerCtx *ctx, IjsPageHeader *ph)
832 {
833   int status;
834
835   ctx->ph = ph;
836   ctx->in_page = FALSE;
837
838   do 
839     {
840       status = ijs_server_iter (ctx);
841     }
842   while (status == 0 && !ctx->in_page);
843
844   ctx->ph = NULL;
845   return status;
846 }
847
848 /**
849  * ijs_server_get_data: Get data from client.
850  * @ctx: The server context.
851  * @buf: Buffer for data being read.
852  * @size: Size of buf.
853  * 
854  * Gets data from client. Data is stored in @buf or the
855  * overflow_buf.
856  *
857  * Return value: Number of bytes read, -1 on end of page, or < 0 on
858  * error.
859  **/
860 int
861 ijs_server_get_data (IjsServerCtx *ctx, char *buf, int size)
862 {
863   int buf_ix = 0;
864   int status = 0;
865
866 #ifdef VERBOSE
867   fprintf (stderr, "ijs_server_get_data %d\n", size);
868 #endif
869
870   if (ctx->overflow_buf != NULL)
871     {
872       int n_bytes = ctx->overflow_buf_size - ctx->overflow_buf_ix;
873       if (n_bytes > size)
874         n_bytes = size;
875       memcpy (buf, ctx->overflow_buf + ctx->overflow_buf_ix, n_bytes);
876       ctx->overflow_buf_ix += n_bytes;
877       buf_ix = n_bytes;
878       if (ctx->overflow_buf_ix == ctx->overflow_buf_size)
879         {
880           free (ctx->overflow_buf);
881           ctx->overflow_buf = NULL;
882           ctx->overflow_buf_size = 0;
883           ctx->overflow_buf_ix = 0;
884         }
885     }
886   ctx->buf = buf;
887   ctx->buf_size = size;
888   ctx->buf_ix = buf_ix;
889   while (!status && ctx->buf_ix < size)
890     {
891       status = ijs_server_iter (ctx);
892     }
893   ctx->buf = NULL;
894   return status;
895 }