1 
2 /*
3  * speaking.c - Speech Dispatcher speech output functions
4  *
5  * Copyright (C) 2001,2002,2003, 2006, 2007 Brailcom, o.p.s
6  *
7  * This is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * This software 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  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  * $Id: speaking.c,v 1.56 2008-10-15 18:06:48 hanke Exp $
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <glib.h>
28 #include <poll.h>
29 #include <unistd.h>
30 #include <safe_io.h>
31 #include "speechd.h"
32 #include "server.h"
33 #include "index_marking.h"
34 #include "symbols.h"
35 #include "module.h"
36 #include "set.h"
37 #include "alloc.h"
38 #include "msg.h"
39 #include "output.h"
40 #include "speaking.h"
41 #include "sem_functions.h"
42 
43 TSpeechDMessage *current_message = NULL;
44 static SPDPriority highest_priority = 0;
45 
46 int SPEAKING = 0;
47 int poll_count;
48 
49 OutputModule *speaking_module;
50 int speaking_uid;
51 int speaking_gid;
52 
53 /* Pause and resume handling */
54 int pause_requested;
55 int pause_requested_fd;
56 int pause_requested_uid;
57 int resume_requested;
58 
59 /*
60   Speak() is responsible for getting right text from right
61   queue in right time and saying it loud through the corresponding
62   synthetiser.  This runs in a separate thread.
63 */
speak(void * data)64 void *speak(void *data)
65 {
66 	TSpeechDMessage *message = NULL;
67 	int ret;
68 	struct pollfd *poll_fds;	/* Descriptors to poll */
69 	struct pollfd main_pfd;
70 	struct pollfd helper_pfd;
71 	int revents;
72 	OutputModule *output;
73 
74 	/* Block all signals and set thread states */
75 	set_speak_thread_attributes();
76 
77 	poll_fds = g_malloc(2 * sizeof(struct pollfd));
78 
79 	main_pfd.fd = speaking_pipe[0];
80 	main_pfd.events = POLLIN;
81 	main_pfd.revents = 0;
82 
83 	helper_pfd.fd = -1;
84 	helper_pfd.events = POLLIN;
85 	helper_pfd.revents = 0;
86 
87 	poll_fds[0] = main_pfd;
88 	poll_fds[1] = helper_pfd;
89 	poll_count = 1;
90 
91 	while (1) {
92 		ret = poll(poll_fds, poll_count, -1);
93 		MSG(5,
94 		    "Poll in speak() returned socket activity, main_pfd revents=%d, poll_pfd revents=%d",
95 		    poll_fds[0].revents, poll_fds[1].revents);
96 		if ((revents = poll_fds[0].revents)) {
97 			if (revents & POLLIN) {
98 				char buf[1];
99 				MSG(5,
100 				    "wait_for_poll: activity in Speech Dispatcher");
101 				const ssize_t rd_bytes =
102 				    safe_read(poll_fds[0].fd, buf, 1);
103 				if (rd_bytes != 1)
104 					FATAL
105 					    ("read from polled fd: could not read 1 byte");
106 			}
107 		}
108 		if (poll_count > 1) {
109 			if ((revents = poll_fds[1].revents)) {
110 				if (revents & POLLHUP) {
111 					// FIXME: We should handle this more gracefully
112 					FATAL
113 					    ("wait_for_poll: output_module disconnected");
114 				} else if ((revents & POLLIN)
115 					   || (revents & POLLPRI)) {
116 					MSG(5,
117 					    "wait_for_poll: activity on output_module");
118 					/* Check if sb is speaking or they are all silent.
119 					 * If some synthesizer is speaking, we must wait. */
120 					is_sb_speaking();
121 				}
122 			}
123 		}
124 
125 		/* Handle pause requests */
126 		if (pause_requested) {
127 			MSG(4, "Trying to pause...");
128 			if (pause_requested == 1)
129 				speaking_pause_all(pause_requested_fd);
130 			if (pause_requested == 2)
131 				speaking_pause(pause_requested_fd,
132 					       pause_requested_uid);
133 			MSG(4, "Paused...");
134 			pause_requested = 0;
135 			continue;
136 		}
137 
138 		if (SPEAKING) {
139 			MSG(5,
140 			    "Continuing because already speaking in speak()");
141 			continue;
142 		}
143 
144 		/* Handle resume requests */
145 		if (resume_requested) {
146 			GList *gl;
147 
148 			MSG(5, "Resume requested");
149 
150 			/* Is there any message after resume? */
151 			if (g_list_length(MessagePausedList) != 0) {
152 				while (1) {
153 					pthread_mutex_lock(&element_free_mutex);
154 					gl = g_list_find_custom
155 					    (MessagePausedList, (void *)NULL,
156 					     message_nto_speak);
157 					MSG(5,
158 					    "Message insterted back to the queues!");
159 					MessagePausedList =
160 					    g_list_remove_link
161 					    (MessagePausedList, gl);
162 					pthread_mutex_unlock
163 					    (&element_free_mutex);
164 					if ((gl != NULL) && (gl->data != NULL)) {
165 						MSG(5, "Reloading message");
166 						reload_message((TSpeechDMessage
167 								*) gl->data);
168 						/* If this resumed message is the same as current_message, then it gets
169 						 * another trip through the queue.  However, some code later in this
170 						 * function will free current_message, even though it is now requeued!
171 						 * Hence use-after-free.
172 						 * current_message is pretty useless after the requeue, make it NULL. */
173 						if (current_message == gl->data)
174 							current_message = NULL;
175 					} else
176 						break;
177 				}
178 			}
179 			MSG(5, "End of resume processing");
180 			resume_requested = 0;
181 		}
182 
183 		MSG(5, "Locking element_free_mutex in speak()");
184 		pthread_mutex_lock(&element_free_mutex);
185 		/* Handle postponed priority progress message */
186 		check_locked(&element_free_mutex);
187 		if ((g_list_length(last_p5_block) != 0)
188 		    && (g_list_length(MessageQueue->p5) == 0)) {
189 			/* Transfer messages from last_p5_block to priority 2 (message) queue */
190 			while (g_list_length(last_p5_block) != 0) {
191 				GList *item;
192 				item = g_list_first(last_p5_block);
193 				message = item->data;
194 				check_locked(&element_free_mutex);
195 				MessageQueue->p2 =
196 				    g_list_insert_sorted(MessageQueue->p2,
197 							 message, sortbyuid);
198 				last_p5_block =
199 				    g_list_remove_link(last_p5_block, item);
200 				g_list_free1(item);
201 			}
202 			assert(message != NULL);
203 			highest_priority = SPD_MESSAGE;
204 			stop_priority_older_than(SPD_TEXT, message->id);
205 			stop_priority(SPD_NOTIFICATION);
206 			stop_priority(SPD_PROGRESS);
207 			check_locked(&element_free_mutex);
208 			pthread_mutex_unlock(&element_free_mutex);
209 			speaking_semaphore_post();
210 			continue;
211 		} else {
212 			/* Extract the right message from priority queue */
213 			message = get_message_from_queues();
214 			if (message == NULL) {
215 				pthread_mutex_unlock(&element_free_mutex);
216 				MSG(5, "No message in the queue");
217 				continue;
218 			}
219 		}
220 
221 		/* Isn't the parent client of this message paused?
222 		 * If it is, insert the message to the MessagePausedList. */
223 		if (message_nto_speak(message, NULL)) {
224 			MSG(4, "Inserting message to paused list...");
225 			MessagePausedList =
226 			    g_list_append(MessagePausedList, message);
227 			pthread_mutex_unlock(&element_free_mutex);
228 			continue;
229 		}
230 
231 		/* Choose the output module */
232 		output = get_output_module(message);
233 		if (output == NULL) {
234 			MSG(3, "Output module doesn't work...");
235 			output_check_module(output);
236 			pthread_mutex_unlock(&element_free_mutex);
237 			continue;
238 		}
239 
240 		int punct_missing = 0;
241 		if (strcmp(output->name, "flite") == 0 ||
242 		    strcmp(output->name, "dtk-generic") == 0 ||
243 		    strcmp(output->name, "epos-generic") == 0 ||
244 		    strcmp(output->name, "llia_phon-generic") == 0 ||
245 		    strcmp(output->name, "mary-generic") == 0 ||
246 		    strcmp(output->name, "swift-generic") == 0 ||
247 		    strcmp(output->name, "pico") == 0)
248 			/* These don't support punctuation */
249 			/* FIXME: rather make them express it */
250 			punct_missing = 1;
251 
252 		if (message->settings.type == SPD_MSGTYPE_TEXT ||
253 		    message->settings.type == SPD_MSGTYPE_CHAR) {
254 			gchar *normalized = g_utf8_normalize(message->buf, -1,
255 					G_NORMALIZE_ALL_COMPOSE);
256 			if (!normalized) {
257 				MSG(2, "Error: Not UTF-8 valid");
258 				pthread_mutex_unlock(&element_free_mutex);
259 				continue;
260 			}
261 			if (strcmp(message->buf, normalized)) {
262 				MSG(5, "text: Normalized '%s' to '%s'", message->buf, normalized);
263 			}
264 			message->buf = normalized;
265 			insert_symbols(message, punct_missing);
266 		}
267 
268 		/* Insert index marks into textual messages */
269 		if (message->settings.type == SPD_MSGTYPE_TEXT) {
270 			insert_index_marks(message,
271 					   message->settings.ssml_mode);
272 		}
273 
274 		/* Write the message to the output layer. */
275 		ret = output_speak(message, output);
276 
277 		MSG(4, "Message sent to output module");
278 		if (ret == -1) {
279 			MSG(2, "Error: Output module failed");
280 			output_check_module(output);
281 			pthread_mutex_unlock(&element_free_mutex);
282 			continue;
283 		}
284 		if (ret != 0) {
285 			MSG(2,
286 			    "ERROR: Can't say message. Module reported error in speaking: %d",
287 			    ret);
288 			pthread_mutex_unlock(&element_free_mutex);
289 			continue;
290 		}
291 		SPEAKING = 1;
292 
293 		if (speaking_module != NULL) {
294 			poll_count = 2;
295 			helper_pfd.fd = speaking_module->pipe_out[0];
296 			poll_fds[1] = helper_pfd;
297 		}
298 
299 		/* Set the id of the client who is speaking. */
300 		speaking_uid = message->settings.uid;
301 		if (current_message != NULL) {
302 			if (!current_message->settings.paused_while_speaking) {
303 				/* Check if the client who emited this message is disconnected
304 				   by now and this was his last message. If so, delete it's settings
305 				   from fdset */
306 				if (get_client_settings_by_uid
307 				    (current_message->settings.uid)->active ==
308 				    0) {
309 					if (!client_has_messages
310 					    (current_message->settings.uid)
311 					    && (current_message->settings.uid !=
312 						message->settings.uid)) {
313 						/* client_has_messages does not account for message,
314 						   which was just retrieved from the queue.
315 						   We also have to compare the uids of message
316 						   and current_message to be sure that there are
317 						   no outstanding messages. */
318 						MSG(4,
319 						    "Removing client settings for uid %d",
320 						    current_message->
321 						    settings.uid);
322 						remove_client_settings_by_uid
323 						    (current_message->
324 						     settings.uid);
325 					}
326 				}
327 				mem_free_message(current_message);
328 			}
329 		}
330 		current_message = message;
331 
332 		/* Check if the last priority 5 message wasn't said yet */
333 		if (last_p5_block != NULL) {
334 			GList *elem;
335 			TSpeechDMessage *p5_message;
336 			elem = g_list_last(last_p5_block);
337 			if (elem != NULL) {
338 				p5_message = (TSpeechDMessage *) elem->data;
339 				if (p5_message->settings.reparted ==
340 				    message->settings.reparted) {
341 					g_list_foreach(last_p5_block,
342 						       (GFunc) mem_free_message,
343 						       NULL);
344 					g_list_free(last_p5_block);
345 					last_p5_block = NULL;
346 				}
347 			}
348 		}
349 
350 		pthread_mutex_unlock(&element_free_mutex);
351 	}
352 }
353 
reload_message(TSpeechDMessage * msg)354 int reload_message(TSpeechDMessage * msg)
355 {
356 	TFDSetElement *client_settings;
357 	int im;
358 	char *pos;
359 	char *newtext;
360 	char *tptr;
361 
362 	if (msg == NULL) {
363 		MSG(4, "Warning: msg == NULL in reload_message()");
364 		return -1;
365 	}
366 
367 	if (msg->settings.index_mark != NULL) {
368 		MSG(5, "Recovering index mark %s", msg->settings.index_mark);
369 		client_settings = get_client_settings_by_uid(msg->settings.uid);
370 		/* Scroll back to provide context, if required */
371 		/* WARNING: This relies on ordered SD_MARK_BODY index marks! */
372 		MSG(5, "Recovering index mark (number)");
373 		im = strtol(msg->settings.index_mark + SD_MARK_BODY_LEN, &tptr,
374 			    10);
375 		MSG(5, "Recovering index mark (comparing tptr)");
376 		if (msg->settings.index_mark + SD_MARK_BODY_LEN == tptr) {
377 			MSG2(2, "index_marking",
378 			     "ERROR: Invalid index_mark '%s'. Message not reloaded.",
379 			     msg->settings.index_mark);
380 			return -1;
381 		}
382 		MSG(5, "Recovered index mark number: %d", im);
383 
384 		im += client_settings->pause_context;
385 
386 		MSG2(5, "index_marking",
387 		     "Requested index mark (with context) is %d (%s+%d)", im,
388 		     msg->settings.index_mark, client_settings->pause_context);
389 		if (im < 0) {
390 			im = 0;
391 			pos = msg->buf;
392 		} else {
393 			pos = find_index_mark(msg, im);
394 			if (pos == NULL)
395 				return -1;
396 		}
397 
398 		newtext = strip_index_marks(pos, client_settings->ssml_mode);
399 		g_free(msg->buf);
400 
401 		if (newtext == NULL)
402 			return -1;
403 		msg->buf = newtext;
404 		msg->bytes = strlen(msg->buf);
405 
406 		if (queue_message
407 		    (msg, -msg->settings.uid, 0, SPD_MSGTYPE_TEXT, 0) == 0) {
408 			if (SPEECHD_DEBUG)
409 				FATAL("Can't queue message\n");
410 			g_free(msg->buf);
411 			g_free(msg);
412 			return -1;
413 		}
414 
415 		return 0;
416 	} else {
417 		MSG(5, "Index mark unknown, inserting the whole message.");
418 
419 		if (queue_message
420 		    (msg, -msg->settings.uid, 0, SPD_MSGTYPE_TEXT, 0) == 0) {
421 			if (SPEECHD_DEBUG)
422 				FATAL("Can't queue message\n");
423 			g_free(msg->buf);
424 			g_free(msg);
425 			return -1;
426 		}
427 
428 		return 0;
429 	}
430 	return 0;
431 }
432 
speaking_stop(int uid)433 void speaking_stop(int uid)
434 {
435 	TSpeechDMessage *msg;
436 	GList *gl;
437 	GList *queue;
438 	signed int gid = -1;
439 
440 	/* Only act if the currently speaking client is the specified one */
441 	if (get_speaking_client_uid() == uid) {
442 		output_stop();
443 
444 		/* Get the queue where the message being spoken came from */
445 		queue = speaking_get_queue(highest_priority);
446 		if (queue == NULL)
447 			return;
448 
449 		/* Get group ID of the current message */
450 		gl = g_list_last(queue);
451 		if (gl == NULL)
452 			return;
453 		if (gl->data == NULL)
454 			return;
455 
456 		msg = (TSpeechDMessage *) gl->data;
457 		if ((msg->settings.reparted != 0) && (msg->settings.uid == uid)) {
458 			gid = msg->settings.reparted;
459 		} else {
460 			return;
461 		}
462 
463 		while (1) {
464 			gl = g_list_last(queue);
465 			if (gl == NULL) {
466 				speaking_set_queue(highest_priority, queue);
467 				return;
468 			}
469 			if (gl->data == NULL)
470 				return;
471 
472 			msg = (TSpeechDMessage *) gl->data;
473 
474 			if ((msg->settings.reparted == gid)
475 			    && (msg->settings.uid == uid)) {
476 				queue = g_list_remove_link(queue, gl);
477 				assert(gl->data != NULL);
478 				mem_free_message(gl->data);
479 			} else {
480 				speaking_set_queue(highest_priority, queue);
481 				return;
482 			}
483 		}
484 	}
485 }
486 
speaking_stop_all()487 void speaking_stop_all()
488 {
489 	TSpeechDMessage *msg;
490 	GList *gl;
491 	GList *queue;
492 
493 	output_stop();
494 
495 	queue = speaking_get_queue(highest_priority);
496 	if (queue == NULL)
497 		return;
498 
499 	gl = g_list_last(queue);
500 	if (gl == NULL)
501 		return;
502 	assert(gl->data != NULL);
503 	msg = (TSpeechDMessage *) gl->data;
504 
505 	if (msg->settings.reparted == 0) {
506 		return;
507 	}
508 
509 	while (1) {
510 		gl = g_list_last(queue);
511 		if (gl == NULL) {
512 			speaking_set_queue(highest_priority, queue);
513 			return;
514 		}
515 		if (SPEECHD_DEBUG)
516 			assert(gl->data != NULL);
517 
518 		msg = (TSpeechDMessage *) gl->data;
519 		if (msg->settings.reparted == 1) {
520 			queue = g_list_remove_link(queue, gl);
521 			assert(gl->data != NULL);
522 			mem_free_message(gl->data);
523 		} else {
524 			speaking_set_queue(highest_priority, queue);
525 			return;
526 		}
527 	}
528 }
529 
speaking_cancel(int uid)530 void speaking_cancel(int uid)
531 {
532 	pthread_mutex_lock(&element_free_mutex);
533 	speaking_stop(uid);
534 	stop_from_uid(uid);
535 	pthread_mutex_unlock(&element_free_mutex);
536 }
537 
speaking_cancel_all()538 void speaking_cancel_all()
539 {
540 	output_stop();
541 	pthread_mutex_lock(&element_free_mutex);
542 	stop_priority(SPD_IMPORTANT);
543 	stop_priority(SPD_MESSAGE);
544 	stop_priority(SPD_TEXT);
545 	stop_priority(SPD_NOTIFICATION);
546 	stop_priority(SPD_PROGRESS);
547 	pthread_mutex_unlock(&element_free_mutex);
548 }
549 
speaking_pause_all(int fd)550 int speaking_pause_all(int fd)
551 {
552 	int err = 0;
553 	int i;
554 	int uid;
555 
556 	for (i = 1; i <= SpeechdStatus.max_fd; i++) {
557 		uid = get_client_uid_by_fd(i);
558 		if (uid == 0)
559 			continue;
560 		err += speaking_pause(i, uid);
561 	}
562 
563 	if (err > 0)
564 		return 1;
565 	else
566 		return 0;
567 }
568 
speaking_pause(int fd,int uid)569 int speaking_pause(int fd, int uid)
570 {
571 	TFDSetElement *settings;
572 	int ret;
573 
574 	MSG(4, "Pause");
575 
576 	/* Find settings for this particular client */
577 	settings = get_client_settings_by_uid(uid);
578 	if (settings == NULL) {
579 		MSG(4,
580 		    "ERROR: Can't get settings of active client in speaking_pause()!");
581 		return 1;
582 	}
583 	settings->paused = 1;
584 
585 	if (speaking_uid != uid) {
586 		MSG(5, "given uid %d not speaking_uid %d", uid, speaking_uid);
587 		return 0;
588 	}
589 
590 	if (SPEAKING) {
591 		if (current_message == NULL) {
592 			MSG(5, "current_message is null");
593 			return 0;
594 		}
595 
596 		ret = output_pause();
597 		if (ret < 0) {
598 			MSG(5, "output_pause returned %d", ret);
599 			return 0;
600 		}
601 
602 		MSG(5,
603 		    "Including current message into the message paused list");
604 		current_message->settings.paused = 2;
605 		current_message->settings.paused_while_speaking = 1;
606 		if (g_list_find(MessagePausedList, current_message) == NULL)
607 			MessagePausedList =
608 			    g_list_append(MessagePausedList, current_message);
609 	}
610 
611 	return 0;
612 }
613 
speaking_resume_all()614 int speaking_resume_all()
615 {
616 	int err = 0;
617 	int i;
618 	int uid;
619 
620 	for (i = 1; i <= SpeechdStatus.max_fd; i++) {
621 		uid = get_client_uid_by_fd(i);
622 		if (uid == 0)
623 			continue;
624 		err += speaking_resume(uid);
625 	}
626 
627 	if (err > 0)
628 		return 1;
629 	else
630 		return 0;
631 }
632 
speaking_resume(int uid)633 int speaking_resume(int uid)
634 {
635 	TFDSetElement *settings;
636 
637 	/* Find settings for this particular client */
638 	settings = get_client_settings_by_uid(uid);
639 	if (settings == NULL)
640 		return 1;
641 	/* Set it to speak again. */
642 	settings->paused = 0;
643 
644 	resume_requested = 1;
645 	speaking_semaphore_post();
646 
647 	return 0;
648 }
649 
socket_send_msg(int fd,char * msg)650 int socket_send_msg(int fd, char *msg)
651 {
652 	int ret;
653 
654 	assert(msg != NULL);
655 	pthread_mutex_lock(&socket_com_mutex);
656 	MSG2(5, "protocol", "%d:REPLY:|%s|", fd, msg);
657 	ret = write(fd, msg, strlen(msg));
658 	pthread_mutex_unlock(&socket_com_mutex);
659 	if (ret < 0) {
660 		MSG(1, "write() error: %s", strerror(errno));
661 		return -1;
662 	}
663 	return 0;
664 }
665 
report_index_mark(TSpeechDMessage * msg,char * index_mark)666 int report_index_mark(TSpeechDMessage * msg, char *index_mark)
667 {
668 	char *cmd;
669 	int ret;
670 
671 	cmd = g_strdup_printf(EVENT_INDEX_MARK_C "-%d\r\n"
672 			      EVENT_INDEX_MARK_C "-%d\r\n"
673 			      EVENT_INDEX_MARK_C "-%s\r\n"
674 			      EVENT_INDEX_MARK,
675 			      msg->id, msg->settings.uid, index_mark);
676 	ret = socket_send_msg(msg->settings.fd, cmd);
677 	if (ret) {
678 		MSG(1, "ERROR: Can't report index mark!");
679 		return -1;
680 	}
681 	g_free(cmd);
682 	return 0;
683 }
684 
685 #define REPORT_STATE(state, ssip_code, ssip_msg) \
686 	int \
687 	report_ ## state (TSpeechDMessage *msg) \
688 	{ \
689 		char *cmd; \
690 		int ret; \
691 		cmd = g_strdup_printf(ssip_code"-%d\r\n"ssip_code"-%d\r\n"ssip_msg, \
692 		                      msg->id, msg->settings.uid); \
693 		ret = socket_send_msg(msg->settings.fd, cmd); \
694 		if (ret){ \
695 			MSG(2, "ERROR: Can't report index mark!"); \
696 			return -1; \
697 		} \
698 		g_free(cmd); \
699 		return 0; \
700 	}
701 
REPORT_STATE(begin,EVENT_BEGIN_C,EVENT_BEGIN)702 REPORT_STATE(begin, EVENT_BEGIN_C, EVENT_BEGIN)
703     REPORT_STATE(end, EVENT_END_C, EVENT_END)
704     REPORT_STATE(pause, EVENT_PAUSED_C, EVENT_PAUSED)
705     REPORT_STATE(resume, EVENT_RESUMED_C, EVENT_RESUMED)
706     REPORT_STATE(cancel, EVENT_CANCELED_C, EVENT_CANCELED)
707 
708 int is_sb_speaking(void)
709 {
710 	char *index_mark;
711 	TFDSetElement *settings;
712 
713 	MSG(5, "is_sb_speaking(), SPEAKING=%d", SPEAKING);
714 
715 	/* Determine if the current module is still speaking */
716 	if (speaking_module != NULL) {
717 		if (current_message == NULL) {
718 			MSG(1,
719 			    "Error: Current message is NULL in is_sb_speaking()");
720 			return -1;
721 		}
722 		settings = &(current_message->settings);
723 
724 		output_is_speaking(&index_mark);
725 		if (index_mark == NULL)
726 			return SPEAKING = 0;
727 
728 		if (!strcmp(index_mark, "no")) {
729 			g_free(index_mark);
730 			return SPEAKING;
731 		}
732 
733 		MSG(5, "INDEX MARK: %s", index_mark);
734 
735 		if (!strcmp(index_mark, SD_MARK_BODY "begin")) {
736 			SPEAKING = 1;
737 			if (!settings->paused_while_speaking) {
738 				if (settings->notification & SPD_BEGIN)
739 					report_begin(current_message);
740 			} else {
741 				if (settings->notification & SPD_RESUME)
742 					report_resume(current_message);
743 				settings->paused_while_speaking = 0;
744 			}
745 		} else if (!strcmp(index_mark, SD_MARK_BODY "end")) {
746 			SPEAKING = 0;
747 			poll_count = 1;
748 			if (settings->notification & SPD_END)
749 				report_end(current_message);
750 			speaking_semaphore_post();
751 		} else if (!strcmp(index_mark, SD_MARK_BODY "paused")) {
752 			SPEAKING = 0;
753 			poll_count = 1;
754 			if (settings->notification & SPD_PAUSE)
755 				report_pause(current_message);
756 			/* We don't want to free this message in speak() since we will
757 			   later copy it in resume() */
758 			current_message = NULL;
759 		} else if (!strcmp(index_mark, SD_MARK_BODY "stopped")) {
760 			SPEAKING = 0;
761 			poll_count = 1;
762 			if (settings->notification & SPD_CANCEL)
763 				report_cancel(current_message);
764 			speaking_semaphore_post();
765 		} else if (index_mark != NULL) {
766 			if (strncmp(index_mark, SD_MARK_BODY, SD_MARK_BODY_LEN)) {
767 				if (settings->notification & SPD_INDEX_MARKS)
768 					report_index_mark(current_message,
769 							  index_mark);
770 			} else {
771 				MSG(5,
772 				    "Setting current index_mark for the message to %s",
773 				    index_mark);
774 				if (current_message->settings.index_mark !=
775 				    NULL)
776 					g_free(current_message->
777 					       settings.index_mark);
778 				current_message->settings.index_mark =
779 				    g_strdup(index_mark);
780 			}
781 
782 		}
783 		g_free(index_mark);
784 	} else {
785 		MSG(5, "Speaking module is NULL, SPEAKING==%d", SPEAKING);
786 		SPEAKING = 0;
787 	}
788 
789 	if (SPEAKING == 0)
790 		speaking_module = NULL;
791 
792 	return SPEAKING;
793 }
794 
get_speaking_client_uid(void)795 int get_speaking_client_uid(void)
796 {
797 	int speaking = 0;
798 	if (SPEAKING == 0) {
799 		speaking_uid = 0;
800 		return 0;
801 	}
802 	if (speaking_uid != 0) {
803 		speaking = speaking_uid;
804 	}
805 	return speaking;
806 }
807 
queue_remove_message(GList * queue,GList * gl)808 GList *queue_remove_message(GList * queue, GList * gl)
809 {
810 	TSpeechDMessage *msg;
811 	assert(gl != NULL);
812 	assert(gl->data != NULL);
813 	msg = (TSpeechDMessage *) gl->data;
814 	if (msg->settings.notification & SPD_CANCEL)
815 		report_cancel(msg);
816 	mem_free_message(gl->data);
817 	queue = g_list_delete_link(queue, gl);
818 	return queue;
819 }
820 
empty_queue(GList * queue)821 GList *empty_queue(GList * queue)
822 {
823 	int num, i;
824 	GList *gl;
825 
826 	num = g_list_length(queue);
827 	for (i = 0; i <= num - 1; i++) {
828 		gl = g_list_first(queue);
829 		queue = queue_remove_message(queue, gl);
830 	}
831 
832 	return queue;
833 }
834 
empty_queue_by_time(GList * queue,unsigned int uid)835 GList *empty_queue_by_time(GList * queue, unsigned int uid)
836 {
837 	int num, i;
838 	GList *gl, *gln;
839 	TSpeechDMessage *msg;
840 
841 	num = g_list_length(queue);
842 	gl = g_list_first(queue);
843 	for (i = 0; i <= num - 1; i++) {
844 		gln = g_list_next(gl);
845 		if (gl == NULL)
846 			break;
847 		assert(gl->data != NULL);
848 		msg = gl->data;
849 		if (msg->id < uid) {
850 			queue = queue_remove_message(queue, gl);
851 		}
852 		gl = gln;
853 	}
854 
855 	return queue;
856 }
857 
stop_priority(SPDPriority priority)858 int stop_priority(SPDPriority priority)
859 {
860 	GList *queue;
861 
862 	queue = speaking_get_queue(priority);
863 
864 	if (highest_priority == priority) {
865 		output_stop();
866 	}
867 
868 	queue = empty_queue(queue);
869 
870 	speaking_set_queue(priority, queue);
871 
872 	return 0;
873 }
874 
stop_priority_older_than(SPDPriority priority,unsigned int uid)875 int stop_priority_older_than(SPDPriority priority, unsigned int uid)
876 {
877 	GList *queue;
878 
879 	queue = speaking_get_queue(priority);
880 
881 	if (highest_priority == priority) {
882 		output_stop();
883 	}
884 
885 	queue = empty_queue_by_time(queue, uid);
886 
887 	speaking_set_queue(priority, queue);
888 
889 	return 0;
890 }
891 
stop_priority_from_uid(GList * queue,const int uid)892 GList *stop_priority_from_uid(GList * queue, const int uid)
893 {
894 	GList *ret = queue;
895 	GList *gl;
896 
897 	while ((gl = g_list_find_custom(ret, &uid, p_msg_uid_lc)))
898 		ret = queue_remove_message(ret, gl);
899 
900 	return ret;
901 }
902 
stop_from_uid(const int uid)903 void stop_from_uid(const int uid)
904 {
905 	check_locked(&element_free_mutex);
906 	MessageQueue->p1 = stop_priority_from_uid(MessageQueue->p1, uid);
907 	MessageQueue->p2 = stop_priority_from_uid(MessageQueue->p2, uid);
908 	MessageQueue->p3 = stop_priority_from_uid(MessageQueue->p3, uid);
909 	MessageQueue->p4 = stop_priority_from_uid(MessageQueue->p4, uid);
910 	MessageQueue->p5 = stop_priority_from_uid(MessageQueue->p5, uid);
911 }
912 
913 /* Determines if this messages is to be spoken
914  * (returns 1) or its parent client is paused (returns 0).
915  * Note: If you are wondering why it's reversed (not to speak instead
916  * of to speak), it's because we also use this function for
917  * searching through the list. */
message_nto_speak(gconstpointer data,gconstpointer nothing)918 gint message_nto_speak(gconstpointer data, gconstpointer nothing)
919 {
920 	TFDSetElement *global_settings;
921 	TSpeechDMessage *message = (TSpeechDMessage *) data;
922 
923 	/* Is there something in the body of the message? */
924 	if (message == NULL)
925 		return 0;
926 
927 	/* Find global settings for this connection. */
928 	global_settings = get_client_settings_by_fd(message->settings.fd);
929 	if (global_settings == NULL)
930 		return 0;
931 
932 	if (!global_settings->paused)
933 		return 0;
934 	else
935 		return 1;
936 }
937 
set_speak_thread_attributes()938 void set_speak_thread_attributes()
939 {
940 	int ret;
941 	sigset_t all_signals;
942 
943 	ret = sigfillset(&all_signals);
944 	if (ret == 0) {
945 		ret = pthread_sigmask(SIG_BLOCK, &all_signals, NULL);
946 		if (ret != 0)
947 			MSG(1,
948 			    "Can't set signal set, expect problems when terminating!");
949 	} else {
950 		MSG(1,
951 		    "Can't fill signal set, expect problems when terminating!");
952 	}
953 
954 	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
955 	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
956 }
957 
stop_priority_except_first(SPDPriority priority)958 void stop_priority_except_first(SPDPriority priority)
959 {
960 	GList *queue;
961 	GList *gl;
962 	TSpeechDMessage *msg;
963 	GList *gl_next;
964 	int gid;
965 
966 	queue = speaking_get_queue(priority);
967 
968 	gl = g_list_last(queue);
969 
970 	if (gl == NULL)
971 		return;
972 	if (gl->data == NULL)
973 		return;
974 
975 	msg = (TSpeechDMessage *) gl->data;
976 	if (msg->settings.reparted <= 0) {
977 		queue = g_list_remove_link(queue, gl);
978 		speaking_set_queue(priority, queue);
979 
980 		stop_priority(priority);
981 		/* Fill the queue with the list containing only the first message */
982 		speaking_set_queue(priority, gl);
983 	} else {
984 		gid = msg->settings.reparted;
985 
986 		if (highest_priority == priority && speaking_gid != gid) {
987 			output_stop();
988 		}
989 
990 		gl = g_list_first(queue);
991 		while (gl) {
992 			gl_next = g_list_next(gl);
993 			if (gl->data != NULL) {
994 				TSpeechDMessage *msgg = gl->data;
995 				if (msgg->settings.reparted != gid) {
996 					queue = g_list_remove_link(queue, gl);
997 					mem_free_message(msgg);
998 				}
999 			}
1000 			gl = gl_next;
1001 		}
1002 		speaking_set_queue(priority, queue);
1003 	}
1004 
1005 	return;
1006 }
1007 
resolve_priorities(SPDPriority priority)1008 void resolve_priorities(SPDPriority priority)
1009 {
1010 	if (priority == SPD_IMPORTANT) {
1011 		if (SPEAKING && highest_priority != SPD_IMPORTANT)
1012 			output_stop();
1013 		stop_priority(SPD_NOTIFICATION);
1014 		stop_priority(SPD_PROGRESS);
1015 	}
1016 
1017 	if (priority == SPD_MESSAGE) {
1018 		if (SPEAKING && highest_priority != SPD_IMPORTANT
1019 		    && highest_priority != SPD_MESSAGE)
1020 			output_stop();
1021 		stop_priority(SPD_TEXT);
1022 		stop_priority(SPD_NOTIFICATION);
1023 		stop_priority(SPD_PROGRESS);
1024 	}
1025 
1026 	if (priority == SPD_TEXT) {
1027 		stop_priority_except_first(SPD_TEXT);
1028 		stop_priority(SPD_NOTIFICATION);
1029 		stop_priority(SPD_PROGRESS);
1030 	}
1031 
1032 	if (priority == SPD_NOTIFICATION) {
1033 		stop_priority_except_first(SPD_NOTIFICATION);
1034 		if (SPEAKING && highest_priority != SPD_NOTIFICATION)
1035 			stop_priority(SPD_NOTIFICATION);
1036 	}
1037 
1038 	if (priority == SPD_PROGRESS) {
1039 		stop_priority(SPD_NOTIFICATION);
1040 		if (SPEAKING) {
1041 			GList *gl;
1042 			check_locked(&element_free_mutex);
1043 			gl = g_list_last(MessageQueue->p5);
1044 			check_locked(&element_free_mutex);
1045 			MessageQueue->p5 =
1046 			    g_list_remove_link(MessageQueue->p5, gl);
1047 			if (gl != NULL) {
1048 				check_locked(&element_free_mutex);
1049 				MessageQueue->p5 =
1050 				    empty_queue(MessageQueue->p5);
1051 				if (gl->data != NULL) {
1052 					MessageQueue->p5 = gl;
1053 				}
1054 			}
1055 		}
1056 	}
1057 
1058 }
1059 
get_message_from_queues()1060 TSpeechDMessage *get_message_from_queues()
1061 {
1062 	GList *gl;
1063 	SPDPriority prio;
1064 	TSpeechDMessage *message;
1065 
1066 	/* We will descend through priorities to say more important
1067 	   messages first. */
1068 	for (prio = SPD_IMPORTANT; prio <= SPD_PROGRESS; prio++) {
1069 		GList *current_queue = speaking_get_queue(prio);
1070 		check_locked(&element_free_mutex);
1071 		gl = g_list_first(current_queue);
1072 
1073 		while (gl != NULL) {
1074 			if (message_nto_speak
1075 			    ((TSpeechDMessage *) gl->data, NULL)) {
1076 				gl = g_list_next(gl);
1077 				continue;
1078 			}
1079 			speaking_set_queue(prio,
1080 					   g_list_remove_link(current_queue,
1081 							      gl));
1082 			highest_priority = prio;
1083 			message = gl->data;
1084 			g_list_free(gl);
1085 			return (TSpeechDMessage *) message;
1086 		}
1087 	}
1088 
1089 	return NULL;
1090 }
1091 
message_has_uid(gconstpointer msg,gconstpointer uid)1092 gint message_has_uid(gconstpointer msg, gconstpointer uid)
1093 {
1094 	if (((TSpeechDMessage *) msg)->settings.uid == *(int *)uid)
1095 		return 0;
1096 	else
1097 		return 1;
1098 }
1099 
1100 /* Return 1 if any message from this client is found
1101    in any of the queues, otherwise return 0 */
client_has_messages(int uid)1102 int client_has_messages(int uid)
1103 {
1104 
1105 	if (g_list_find_custom
1106 	    (MessageQueue->p5, (gconstpointer) & uid, message_has_uid)
1107 	    || g_list_find_custom(MessageQueue->p4, (gconstpointer) & uid,
1108 				  message_has_uid)
1109 	    || g_list_find_custom(MessageQueue->p3, (gconstpointer) & uid,
1110 				  message_has_uid)
1111 	    || g_list_find_custom(MessageQueue->p2, (gconstpointer) & uid,
1112 				  message_has_uid)
1113 	    || g_list_find_custom(MessageQueue->p1, (gconstpointer) & uid,
1114 				  message_has_uid))
1115 		return 1;
1116 	else
1117 		return 0;
1118 }
1119 
speaking_get_queue(SPDPriority priority)1120 GList *speaking_get_queue(SPDPriority priority)
1121 {
1122 	GList *queue = NULL;
1123 
1124 	assert(priority >= SPD_IMPORTANT && priority <= SPD_PROGRESS);
1125 
1126 	check_locked(&element_free_mutex);
1127 	switch (priority) {
1128 	case SPD_IMPORTANT:
1129 		queue = MessageQueue->p1;
1130 		break;
1131 	case SPD_MESSAGE:
1132 		queue = MessageQueue->p2;
1133 		break;
1134 	case SPD_TEXT:
1135 		queue = MessageQueue->p3;
1136 		break;
1137 	case SPD_NOTIFICATION:
1138 		queue = MessageQueue->p4;
1139 		break;
1140 	case SPD_PROGRESS:
1141 		queue = MessageQueue->p5;
1142 		break;
1143 	}
1144 
1145 	return queue;
1146 }
1147 
speaking_set_queue(SPDPriority priority,GList * queue)1148 void speaking_set_queue(SPDPriority priority, GList * queue)
1149 {
1150 	assert(priority >= SPD_IMPORTANT && priority <= SPD_PROGRESS);
1151 
1152 	check_locked(&element_free_mutex);
1153 	switch (priority) {
1154 	case SPD_IMPORTANT:
1155 		MessageQueue->p1 = queue;
1156 		break;
1157 	case SPD_MESSAGE:
1158 		MessageQueue->p2 = queue;
1159 		break;
1160 	case SPD_TEXT:
1161 		MessageQueue->p3 = queue;
1162 		break;
1163 	case SPD_NOTIFICATION:
1164 		MessageQueue->p4 = queue;
1165 		break;
1166 	case SPD_PROGRESS:
1167 		MessageQueue->p5 = queue;
1168 		break;
1169 	}
1170 }
1171 
sortbyuid(gconstpointer a,gconstpointer b)1172 gint sortbyuid(gconstpointer a, gconstpointer b)
1173 {
1174 	const TSpeechDMessage *msg1 = a;
1175 	const TSpeechDMessage *msg2 = b;
1176 
1177 	if ((msg1 == NULL) && (msg2 != NULL))
1178 		return -1;
1179 	if ((msg1 != NULL) && (msg2 == NULL))
1180 		return +1;
1181 	if ((msg1 == NULL) && (msg2 == NULL))
1182 		return 0;
1183 
1184 	return msg1->id - msg2->id;
1185 }
1186