handle the server just killing us sometimes
[cycon.git] / main.c
blobe8f31fd31515352404eaafb0aaa3068251160c47
1 /*
2 * Copyright (c) 2018, De Rais <derais@cock.li>
4 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all
7 * copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
19 #include <locale.h>
20 #include <stdio.h>
21 #include <unistd.h>
23 #include <libwebsockets.h>
24 #include <pthread.h>
26 #include "cycon.h"
27 #include "macros.h"
29 const char *program_name = "cycon";
31 /* Usage message */
32 static void usage(void)
34 int len = strlen(program_name) + 7;
36 fprintf(stderr, "usage: %s [ -s SERVER ] # like \"cytu.be\"\n",
37 program_name);
38 fprintf(stderr, "%*s [ -h ] # if website is http (not https)\n",
39 len, "");
40 fprintf(stderr, "%*s -c CHANNEL\n", len, "");
43 /* LWS handling thread */
44 extern int
45 cytube_lws_handler(struct lws *wsi, enum lws_callback_reasons reason,
46 void *user, void *in, size_t len);
47 void * lws_spin(void *arg)
49 int lret = 0;
50 time_t now = 0;
51 struct state volatile *s = (struct state volatile *) arg;
52 char *ripped_host = 0;
53 const struct lws_protocols p[] = {
54 /* */
56 /* */
57 .name = "sync", /* */
58 .callback = cytube_lws_handler, /* */
59 .per_session_data_size = sizeof(struct state), /* */
60 .rx_buffer_size = 0, /* */
61 .id = 0, /* */
62 .user = (void *) s, /* */
63 .tx_packet_size = 0, /* */
64 }, { 0 },
66 struct lws_context_creation_info info = {
67 /* */
68 .options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT, /* */
69 .port = CONTEXT_PORT_NO_LISTEN, /* */
70 .protocols = p, /* */
72 struct lws_context *context = 0;
73 struct lws *wsi = 0;
74 int sslf = s->https ? LCCSCF_USE_SSL | LCCSCF_ALLOW_SELFSIGNED : 0;
75 struct lws_client_connect_info cci = {
76 /* */
77 .context = 0, /* */
78 .ssl_connection = sslf, /* */
79 .address = s->address, /* */
80 .origin = s->address, /* */
81 .protocol = "sync", /* */
82 .userdata = (void *) &s, /* */
83 .pwsi = &wsi, /* */
86 /* First, HTTP stuff in preparation for LWS */
87 if (cytube_get_session_cookie(cci.address, (s->https ? "https" :
88 "http"), s->channel_name,
89 s) < 0) {
90 ERROR_MESSAGE("cannot extract ip-session cookie");
91 ERROR_MESSAGE("perhaps \"%s\" is not a channel?",
92 s->channel_name);
93 s->please_die = 1;
94 pthread_exit(0);
97 /* See cb_get_real_server */
98 if (s->https) {
99 ripped_host = strdup(s->socket_host + 8);
100 } else {
101 ripped_host = strdup(s->socket_host + 7);
104 if (!(ripped_host)) {
105 goto done;
108 for (char *p = ripped_host; *p; ++p) {
109 if (*p == ':') {
110 *p = 0;
111 cci.port = strtoll(p + 1, 0, 0);
112 break;
116 cci.host = ripped_host;
117 cci.address = ripped_host;
119 /* Start up LWS XXX: change log_level to 0 */
120 lws_set_log_level(0, 0);
121 cytube_set_cci_and_s(&cci, s);
123 if (!(context = lws_create_context(&info))) {
124 ERROR_MESSAGE("lws_create_context failed");
125 s->please_die = 1;
126 pthread_exit(0);
129 cci.context = context;
130 s->last_playlist_req = time(0) + 120;
132 while (!s->please_die) {
133 if ((lret = lws_service(context, 1000)) < 0) {
134 break;
137 /* Do we need to ping? */
138 now = time(0);
140 if (s->established) {
141 if (s->last_ping + (s->ping_interval / 1000) <= now) {
142 s->must_write_ping = 1;
143 s->last_ping = now;
146 if (s->last_playlist_req + 120 <= now) {
147 s->must_ask_for_playlist = 1;
148 s->last_playlist_req = now;
152 /* Tell LWS we want to write */
153 if (s->must_write_upgrade ||
154 s->must_write_ping ||
155 s->must_join_channel ||
156 s->must_ask_for_playlist) {
157 lws_callback_on_writable(wsi);
161 lws_context_destroy(context);
162 done:
163 free(ripped_host);
165 return 0;
168 /* Main method */
169 int main(int argc, char **argv)
171 int ret = -1;
172 struct state s = {
173 /* */
174 .address = "cytu.be", .https = 1,
176 char *server_arg = 0;
177 char *channel_arg = 0;
178 int opt = 0;
179 pthread_t lws_thread = { 0 };
181 setlocale(LC_ALL, "");
183 /* Parse arguments */
184 while ((opt = getopt(argc, argv, "s:c:h")) != -1) {
185 switch (opt) {
186 case 's':
187 server_arg = optarg;
188 break;
189 case 'c':
190 channel_arg = optarg;
191 break;
192 case 'h':
193 s.https = 0;
194 break;
195 default:
196 usage();
198 return -1;
202 if (server_arg) {
203 s.address = server_arg;
206 if (!channel_arg) {
207 usage();
208 goto done;
211 s.channel_name = channel_arg;
212 s.playlist.current_playing_uid = -1;
214 if (ui_init(&s) < 0) {
215 PERROR_MESSAGE("ui initialization error");
216 goto done;
219 if (pthread_create(&lws_thread, 0, lws_spin, (void *) &s)) {
220 PERROR_MESSAGE("pthread_create");
221 goto done;
224 ui_loop(&s);
225 s.please_die = 1;
226 ret = 0;
227 pthread_join(lws_thread, 0);
228 done:
229 state_clean(&s);
231 return ret;