2 * Copyright (c) 2001-2004 artofcode LLC.
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:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
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
31 #include "ijs_server.h"
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)
47 struct _IjsServerCtx {
50 IjsSendChan send_chan;
51 IjsRecvChan recv_chan;
55 IjsBeginJobCb *begin_cb;
59 IjsQueryStatusCb *status_cb;
61 IjsListParamsCb *list_cb;
63 IjsEnumParamCb *enum_cb;
65 IjsSetParamCb *set_cb;
67 IjsGetParamCb *get_cb;
75 /* This should be IjsFieldsSet, but David Suffield reports that this
76 causes problems when compiling with g++. */
84 int overflow_buf_size;
89 ijs_server_dummy_begin_cb (void *begin_cb_data,
97 ijs_server_dummy_end_cb (void *end_cb_data,
105 ijs_server_init (void)
111 IjsServerCtx *ctx = (IjsServerCtx *)malloc (sizeof(IjsServerCtx));
114 memcpy (resp_buf, IJS_RESP_STR, sizeof(resp_buf));
119 _setmode(fd_from, _O_BINARY);
120 _setmode(fd_to, _O_BINARY);
123 fprintf (stderr, "fd_from = %d, fd_to = %d\n", fd_from, fd_to);
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));
129 if (nbytes != sizeof(helo_buf))
133 nbytes = write (ctx->send_chan.fd, resp_buf, sizeof(resp_buf));
134 if (nbytes != sizeof(resp_buf))
141 ctx->in_page = FALSE;
143 ctx->overflow_buf = NULL;
145 ctx->begin_cb = ijs_server_dummy_begin_cb;
146 ctx->end_cb = ijs_server_dummy_end_cb;
152 ijs_server_done (ctx);
158 ijs_server_install_begin_cb (IjsServerCtx *ctx,
159 IjsBeginJobCb *begin_cb, void *begin_cb_data)
161 ctx->begin_cb = begin_cb;
162 ctx->begin_cb_data = begin_cb_data;
167 ijs_server_install_end_cb (IjsServerCtx *ctx,
168 IjsEndJobCb *end_cb, void *end_cb_data)
170 ctx->end_cb = end_cb;
171 ctx->end_cb_data = end_cb_data;
176 ijs_server_install_status_cb (IjsServerCtx *ctx,
177 IjsQueryStatusCb *status_cb,
178 void *status_cb_data)
180 ctx->status_cb = status_cb;
181 ctx->status_cb_data = status_cb_data;
186 ijs_server_install_list_cb (IjsServerCtx *ctx,
187 IjsListParamsCb *list_cb, void *list_cb_data)
189 ctx->list_cb = list_cb;
190 ctx->list_cb_data = list_cb_data;
195 ijs_server_install_enum_cb (IjsServerCtx *ctx,
196 IjsEnumParamCb *enum_cb, void *enum_cb_data)
198 ctx->enum_cb = enum_cb;
199 ctx->enum_cb_data = enum_cb_data;
204 ijs_server_install_set_cb (IjsServerCtx *ctx,
205 IjsSetParamCb *set_cb, void *set_cb_data)
207 ctx->set_cb = set_cb;
208 ctx->set_cb_data = set_cb_data;
213 ijs_server_install_get_cb (IjsServerCtx *ctx,
214 IjsGetParamCb *get_cb, void *get_cb_data)
216 ctx->get_cb = get_cb;
217 ctx->get_cb_data = get_cb_data;
222 ijs_server_ack (IjsServerCtx *ctx)
226 status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
229 return ijs_send_buf (&ctx->send_chan);
233 ijs_server_done (IjsServerCtx *ctx)
235 /* todo: close channels */
236 ijs_server_ack (ctx);
242 ijs_server_nak (IjsServerCtx *ctx, int errorcode)
246 status = ijs_send_begin (&ctx->send_chan, IJS_CMD_NAK);
249 status = ijs_send_int (&ctx->send_chan, errorcode);
252 return ijs_send_buf (&ctx->send_chan);
255 /* The return code is: 0 if ok, positive on normal exit, negative on error */
256 typedef int (*ijs_server_proc) (IjsServerCtx *ctx);
259 ijs_server_proc_ack (IjsServerCtx *ctx)
261 /* servers should not get ack commands */
266 ijs_server_proc_nak (IjsServerCtx *ctx)
268 /* servers should not get nak commands */
273 ijs_server_proc_ping (IjsServerCtx *ctx)
278 status = ijs_recv_int (&ctx->recv_chan, &version);
281 if (version > IJS_VERSION)
282 version = IJS_VERSION;
283 ctx->version = version;
286 fprintf (stderr, "ping version=%d\n", version);
288 status = ijs_send_begin (&ctx->send_chan, IJS_CMD_PONG);
291 status = ijs_send_int (&ctx->send_chan, IJS_VERSION);
294 return ijs_send_buf (&ctx->send_chan);
298 ijs_server_proc_pong (IjsServerCtx *ctx)
300 /* servers should not get pong commands */
305 ijs_server_proc_open (IjsServerCtx *ctx)
307 /* A server might allocate tables here. */
308 return ijs_server_ack (ctx);
312 ijs_server_proc_close (IjsServerCtx *ctx)
314 /* A server might deallocate memory here. */
315 return ijs_server_ack (ctx);
319 ijs_server_proc_begin_job (IjsServerCtx *ctx)
324 code = ijs_recv_int (&ctx->recv_chan, &job_id);
325 if (code < 0) return code;
328 return ijs_server_nak (ctx, IJS_ETOOMANYJOBS);
330 ctx->job_id = job_id;
332 return ijs_server_ack (ctx);
336 ijs_server_proc_end_job (IjsServerCtx *ctx)
341 code = ijs_recv_int (&ctx->recv_chan, &job_id);
342 if (code < 0) return code;
344 if (!ctx->in_job || job_id != ctx->job_id)
345 return ijs_server_nak (ctx, IJS_EJOBID);
348 return ijs_server_ack (ctx);
352 ijs_server_proc_cancel_job (IjsServerCtx *ctx)
357 code = ijs_recv_int (&ctx->recv_chan, &job_id);
358 if (code < 0) return code;
360 if (!ctx->in_job || job_id != ctx->job_id)
361 return ijs_server_nak (ctx, IJS_EJOBID);
362 /* todo: call cancel callback here */
365 return ijs_server_ack (ctx);
369 ijs_server_proc_query_status (IjsServerCtx *ctx)
374 code = ijs_recv_int (&ctx->recv_chan, &job_id);
378 if (!ctx->in_job || ctx->job_id != job_id)
379 return ijs_server_nak (ctx, IJS_EJOBID);
381 code = ctx->status_cb (ctx->list_cb_data, ctx, job_id);
383 return ijs_server_nak (ctx, code);
388 status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
391 status = ijs_send_int (&ctx->send_chan, code);
394 return ijs_send_buf (&ctx->send_chan);
399 ijs_server_proc_list_params (IjsServerCtx *ctx)
405 code = ijs_recv_int (&ctx->recv_chan, &job_id);
409 if (!ctx->in_job || ctx->job_id != job_id)
410 return ijs_server_nak (ctx, IJS_EJOBID);
412 code = ctx->list_cb (ctx->list_cb_data, ctx, job_id, buf, sizeof(buf));
414 return ijs_server_nak (ctx, code);
419 status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
422 status = ijs_send_block (&ctx->send_chan, buf, code);
425 return ijs_send_buf (&ctx->send_chan);
430 ijs_server_proc_enum_param (IjsServerCtx *ctx)
438 code = ijs_recv_int (&ctx->recv_chan, &job_id);
442 if (!ctx->in_job || ctx->job_id != job_id)
443 return ijs_server_nak (ctx, IJS_EJOBID);
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])
450 fprintf (stderr, "ijs_server_proc_enum_param, key_size = %d\n", key_size);
453 code = ctx->enum_cb (ctx->enum_cb_data, ctx, job_id, key, buf, sizeof(buf));
455 return ijs_server_nak (ctx, code);
460 status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
463 status = ijs_send_block (&ctx->send_chan, buf, code);
466 return ijs_send_buf (&ctx->send_chan);
471 ijs_strnlen (const char *s, int size)
475 for (i = 0; i < size; i++)
482 ijs_server_parse_int (const char *value, int size, int *result)
500 for (; i < size; i++)
503 if (c < '0' || c > '9')
505 num = (num * 10) + (c - '0');
512 ijs_server_parse_float (const char *value, int size, double *result)
517 if (size + 1 > sizeof(buf))
519 memcpy (buf, value, size);
521 *result = strtod (buf, &tail);
528 ijs_server_set_param (IjsServerCtx *ctx, IjsJobId job_id, const char *key,
529 const char *value, int value_size)
534 fprintf (stderr, "set_param %s = ", key);
535 fwrite (value, 1, value_size, stderr);
536 fputs ("\n", stderr);
538 if (!strcmp (key, "NumChan"))
540 code = ijs_server_parse_int (value, value_size, &ctx->ph->n_chan);
542 ctx->fields_set |= IJS_N_CHAN_SET;
545 else if (!strcmp (key, "BitsPerSample"))
547 code = ijs_server_parse_int (value, value_size, &ctx->ph->bps);
549 ctx->fields_set |= IJS_BPS_SET;
552 else if (!strcmp (key, "ColorSpace"))
554 int size = value_size;
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;
563 else if (!strcmp (key, "Width"))
565 code = ijs_server_parse_int (value, value_size, &ctx->ph->width);
567 ctx->fields_set |= IJS_WIDTH_SET;
570 else if (!strcmp (key, "Height"))
572 code = ijs_server_parse_int (value, value_size, &ctx->ph->height);
574 ctx->fields_set |= IJS_HEIGHT_SET;
577 else if (!strcmp (key, "Dpi"))
581 for (x_ix = 0; x_ix < value_size; x_ix++)
582 if (value[x_ix] == 'x')
584 if (x_ix == value_size)
586 code = ijs_server_parse_float (value, x_ix, &ctx->ph->xres);
589 code = ijs_server_parse_float (value + x_ix + 1, value_size - (x_ix + 1),
593 ctx->fields_set |= IJS_DPI_SET;
598 return ctx->set_cb (ctx->set_cb_data, ctx, job_id, key, value, value_size);
603 ijs_server_proc_set_param (IjsServerCtx *ctx)
605 const char *key, *value;
606 int key_size, value_size;
611 code = ijs_recv_int (&ctx->recv_chan, &job_id);
615 if (!ctx->in_job || ctx->job_id != job_id)
616 return ijs_server_nak (ctx, IJS_EJOBID);
618 code = ijs_recv_int (&ctx->recv_chan, ¶m_size);
621 if (param_size != ctx->recv_chan.buf_size - ctx->recv_chan.buf_idx)
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)
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);
631 return ijs_server_nak (ctx, code);
633 return ijs_server_ack (ctx);
637 ijs_server_get_param (IjsServerCtx *ctx, IjsJobId job_id, const char *key,
638 char *value, int value_size)
641 fprintf (stderr, "ijs_server_get_param %s\n", key);
643 return ctx->get_cb (ctx->get_cb_data, ctx, job_id, key,
648 ijs_server_proc_get_param (IjsServerCtx *ctx)
656 code = ijs_recv_int (&ctx->recv_chan, &job_id);
660 if (!ctx->in_job || ctx->job_id != job_id)
661 return ijs_server_nak (ctx, IJS_EJOBID);
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])
668 fprintf (stderr, "ijs_server_proc_get_param, key_size = %d\n", key_size);
671 code = ijs_server_get_param (ctx, job_id, key, buf, sizeof(buf));
673 return ijs_server_nak (ctx, code);
678 status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
681 status = ijs_send_block (&ctx->send_chan, buf, code);
684 return ijs_send_buf (&ctx->send_chan);
689 ijs_server_proc_begin_page (IjsServerCtx *ctx)
691 IjsPageHeader *ph = ctx->ph;
696 if ((ctx->fields_set & IJS_FIELDS_REQUIRED) != IJS_FIELDS_REQUIRED)
699 fprintf (stderr, "begin page %d %d %d %d %d\n",
700 ph->n_chan, ph->bps, ph->cs, ph->width, ph->height);
705 return ijs_server_ack (ctx);
708 return ijs_server_nak (ctx, status);
712 ijs_server_read_data (IjsServerCtx *ctx, char *buf, int size)
716 status = ijs_recv_read (&ctx->recv_chan, buf, size);
717 return (status == size) ? 0 : IJS_EIO;
721 ijs_server_proc_send_data_block (IjsServerCtx *ctx)
727 status = ijs_recv_int (&ctx->recv_chan, &job_id);
728 if (status < 0) return status;
730 if (!ctx->in_job || job_id != ctx->job_id)
732 else if (ctx->buf == NULL)
735 if (!status) status = ijs_recv_int (&ctx->recv_chan, &size);
738 fprintf (stderr, "status=%d, send data block id=%d, size=%d\n",
739 status, job_id, size);
742 return ijs_server_nak (ctx, status);
744 if (size <= ctx->buf_size - ctx->buf_ix)
746 status = ijs_server_read_data (ctx, ctx->buf + ctx->buf_ix, size);
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;
759 status = ijs_server_read_data (ctx, ctx->overflow_buf,
760 ctx->overflow_buf_size);
763 return ijs_server_ack (ctx);
767 ijs_server_proc_end_page (IjsServerCtx *ctx)
770 fprintf (stderr, "end page\n");
772 return ijs_server_ack (ctx);
776 ijs_server_proc_exit (IjsServerCtx *ctx)
781 ijs_server_proc ijs_server_procs[] = {
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,
803 ijs_server_iter (IjsServerCtx *ctx)
808 status = ijs_recv_buf (&ctx->recv_chan);
813 cmd_num = ijs_get_int (ctx->recv_chan.buf);
815 fprintf (stderr, "command %d, %d bytes\n", cmd_num, ctx->recv_chan.buf_size);
818 cmd_num >= (int)sizeof(ijs_server_procs) / sizeof(ijs_server_procs[0]))
820 return ijs_server_procs[cmd_num] (ctx);
824 * ijs_server_get_page_header: Get the page header.
825 * @ctx: The server context.
826 * @ph: Where to store the page header.
828 * Return value: 0 on success, 1 on normal exit, negative on error.
831 ijs_server_get_page_header (IjsServerCtx *ctx, IjsPageHeader *ph)
836 ctx->in_page = FALSE;
840 status = ijs_server_iter (ctx);
842 while (status == 0 && !ctx->in_page);
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.
854 * Gets data from client. Data is stored in @buf or the
857 * Return value: Number of bytes read, -1 on end of page, or < 0 on
861 ijs_server_get_data (IjsServerCtx *ctx, char *buf, int size)
867 fprintf (stderr, "ijs_server_get_data %d\n", size);
870 if (ctx->overflow_buf != NULL)
872 int n_bytes = ctx->overflow_buf_size - ctx->overflow_buf_ix;
875 memcpy (buf, ctx->overflow_buf + ctx->overflow_buf_ix, n_bytes);
876 ctx->overflow_buf_ix += n_bytes;
878 if (ctx->overflow_buf_ix == ctx->overflow_buf_size)
880 free (ctx->overflow_buf);
881 ctx->overflow_buf = NULL;
882 ctx->overflow_buf_size = 0;
883 ctx->overflow_buf_ix = 0;
887 ctx->buf_size = size;
888 ctx->buf_ix = buf_ix;
889 while (!status && ctx->buf_ix < size)
891 status = ijs_server_iter (ctx);