make pkg-config overridable
[cycon.git] / main.c
blob18968be81e2d4b77b1a86fa1fab70f6aa6c1f23d
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
33 usage(void)
35 int len = strlen(program_name) + 7;
37 fprintf(stderr, "usage: %s [ -s SERVER ] # like \"cytu.be\"\n",
38 program_name);
39 fprintf(stderr, "%*s [ -h ] # if website is http (not https)\n",
40 len, "");
41 fprintf(stderr, "%*s -c CHANNEL\n", len, "");
44 /* LWS handling thread */
45 extern int cytube_lws_handler(struct lws *wsi, enum lws_callback_reasons reason,
46 void *user, void *in, size_t len);
47 void *
48 lws_spin(void *arg)
50 int lret = 0;
51 time_t now = 0;
52 struct state volatile *s = (struct state volatile *) arg;
53 char *ripped_host = 0;
54 const struct lws_protocols p[] = {
55 /* */
57 /* */
58 .name = "sync", /* */
59 .callback = cytube_lws_handler, /* */
60 .per_session_data_size = sizeof(struct state), /* */
61 .rx_buffer_size = 0, /* */
62 .id = 0, /* */
63 .user = (void *) s, /* */
64 .tx_packet_size = 0, /* */
65 }, { 0 },
67 struct lws_context_creation_info info = {
68 /* */
69 .options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT, /* */
70 .port = CONTEXT_PORT_NO_LISTEN, /* */
71 .protocols = p, /* */
73 struct lws_context *context = 0;
74 struct lws *wsi = 0;
75 int sslf = s->https ? LCCSCF_USE_SSL | LCCSCF_ALLOW_SELFSIGNED : 0;
76 struct lws_client_connect_info cci = {
77 /* */
78 .context = 0, /* */
79 .ssl_connection = sslf, /* */
80 .address = s->address, /* */
81 .origin = s->address, /* */
82 .protocol = "sync", /* */
83 .userdata = (void *) &s, /* */
84 .pwsi = &wsi, /* */
87 /* First, HTTP stuff in preparation for LWS */
88 if (cytube_get_session_cookie(cci.address, (s->https ? "https" :
89 "http"), s->channel_name,
90 s) < 0) {
91 ERROR_MESSAGE("cannot extract ip-session cookie");
92 ERROR_MESSAGE("perhaps \"%s\" is not a channel?",
93 s->channel_name);
94 s->please_die = 1;
95 pthread_exit(0);
98 /* See cb_get_real_server */
99 if (s->https) {
100 ripped_host = strdup(s->socket_host + 8);
101 } else {
102 ripped_host = strdup(s->socket_host + 7);
105 if (!(ripped_host)) {
106 goto done;
109 for (char *p = ripped_host; *p; ++p) {
110 if (*p == ':') {
111 *p = 0;
112 cci.port = strtoll(p + 1, 0, 0);
113 break;
117 cci.host = ripped_host;
118 cci.address = ripped_host;
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 */
170 main(int argc, char **argv)
172 int ret = -1;
173 struct state s = {
174 /* */
175 .address = "cytu.be", .https = 1,
177 char *server_arg = 0;
178 char *channel_arg = 0;
179 int opt = 0;
180 pthread_t lws_thread = { 0 };
182 setlocale(LC_ALL, "");
184 /* Parse arguments */
185 while ((opt = getopt(argc, argv, "s:c:h")) != -1) {
186 switch (opt) {
187 case 's':
188 server_arg = optarg;
189 break;
190 case 'c':
191 channel_arg = optarg;
192 break;
193 case 'h':
194 s.https = 0;
195 break;
196 default:
197 usage();
199 return -1;
203 if (server_arg) {
204 s.address = server_arg;
207 if (!channel_arg) {
208 usage();
209 goto done;
212 s.channel_name = channel_arg;
213 s.playlist.current_playing_uid = -1;
215 if (ui_init(&s) < 0) {
216 PERROR_MESSAGE("ui initialization error");
217 goto done;
220 if (pthread_create(&lws_thread, 0, lws_spin, (void *) &s)) {
221 PERROR_MESSAGE("pthread_create");
222 goto done;
225 ui_loop(&s);
226 s.please_die = 1;
227 ret = 0;
228 pthread_join(lws_thread, 0);
229 done:
230 state_clean(&s);
232 return ret;