1 /* -*- c-basic-offset: 8; -*- */
2 /* queue.c: Implementation data queue logic.
3  *
4  *  Copyright (C) 2002-2004 the Icecast team <team@icecast.org>,
5  *  Copyright (C) 2012-2019 Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Library General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Library General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Library General Public
18  *  License along with this library; if not, write to the Free
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * $Id$
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #   include <config.h>
26 #endif
27 
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdio.h>
31 
32 #include <shout/shout.h>
33 #include "shout_private.h"
34 
35 /* queue data in pages of SHOUT_BUFSIZE bytes */
shout_queue_data(shout_queue_t * queue,const unsigned char * data,size_t len)36 int shout_queue_data(shout_queue_t *queue, const unsigned char *data, size_t len)
37 {
38     shout_buf_t *buf;
39     size_t       plen;
40 
41     if (!len)
42         return SHOUTERR_SUCCESS;
43 
44     if (!queue->len) {
45         queue->head = calloc(1, sizeof(shout_buf_t));
46         if (!queue->head)
47             return SHOUTERR_MALLOC;
48     }
49 
50     for (buf = queue->head; buf->next; buf = buf->next) ;
51 
52     /* Maybe any added data should be freed if we hit a malloc error?
53      * Otherwise it'd be impossible to tell where to start requeueing.
54      * (As if anyone ever tried to recover from a malloc error.) */
55     while (len > 0) {
56         if (buf->len == SHOUT_BUFSIZE) {
57             buf->next = calloc(1, sizeof(shout_buf_t));
58             if (!buf->next)
59                 return SHOUTERR_MALLOC;
60             buf->next->prev = buf;
61             buf = buf->next;
62         }
63 
64         plen = len > SHOUT_BUFSIZE - buf->len ? SHOUT_BUFSIZE - buf->len : len;
65         memcpy(buf->data + buf->len, data, plen);
66         buf->len += plen;
67         data += plen;
68         len -= plen;
69         queue->len += plen;
70     }
71     return SHOUTERR_SUCCESS;
72 }
73 
shout_queue_str(shout_connection_t * self,const char * str)74 int shout_queue_str(shout_connection_t *self, const char *str)
75 {
76     return shout_queue_data(&self->wqueue, (const unsigned char*)str, strlen(str));
77 }
78 
79 /* this should be shared with sock_write. Create libicecommon. */
shout_queue_printf(shout_connection_t * self,const char * fmt,...)80 int shout_queue_printf(shout_connection_t *self, const char *fmt, ...)
81 {
82     char        buffer[1024];
83     char       *buf;
84     va_list     ap, ap_retry;
85     int         len;
86     int         ret = SHOUTERR_SUCCESS;
87 
88     buf = buffer;
89 
90     va_start(ap, fmt);
91     va_copy(ap_retry, ap);
92 
93     len = vsnprintf(buf, sizeof(buffer), fmt, ap);
94 
95     if (len > 0) {
96         if ((size_t)len < sizeof(buffer)) {
97             shout_queue_data(&self->wqueue, (unsigned char*)buf, len);
98         } else {
99             buf = malloc(++len);
100             if (buf) {
101                 len = vsnprintf(buf, len, fmt, ap_retry);
102                 shout_queue_data(&self->wqueue, (unsigned char*)buf, len);
103                 free(buf);
104             } else {
105                 ret = SHOUTERR_MALLOC;
106             }
107         }
108     }
109 
110     va_end(ap_retry);
111     va_end(ap);
112 
113     return ret;
114 }
115 
shout_queue_free(shout_queue_t * queue)116 void shout_queue_free(shout_queue_t *queue)
117 {
118     shout_buf_t *prev;
119 
120     while (queue->head) {
121         prev = queue->head;
122         queue->head = queue->head->next;
123         free(prev);
124     }
125     queue->len = 0;
126 }
127 
128 /* collect nodes of a queue into a single buffer */
shout_queue_collect(shout_buf_t * queue,char ** buf)129 ssize_t shout_queue_collect(shout_buf_t *queue, char **buf)
130 {
131     shout_buf_t *node;
132     size_t       pos = 0;
133     size_t       len = 0;
134 
135     for (node = queue; node; node = node->next)
136         len += node->len;
137 
138     if (!(*buf = malloc(len)))
139         return SHOUTERR_MALLOC;
140 
141     for (node = queue; node; node = node->next) {
142         memcpy(*buf + pos, node->data, node->len);
143         pos += node->len;
144     }
145 
146     return len;
147 }
148