static void satip_discovery_send_msearch(void *aux) { #define MSG "\ M-SEARCH * HTTP/1.1\r\n\ HOST: 239.255.255.250:1900\r\n\ MAN: \"ssdp:discover\"\r\n\ MX: 2\r\n\ ST: urn:ses-com:device:SatIPServer:1\r\n" int attempt = ((intptr_t)aux) % 10; htsbuf_queue_t q; /* UDP is not reliable - send this message three times */ if (attempt < 1 || attempt > 3) return; if (satip_discovery_service == NULL) return; htsbuf_queue_init(&q, 0); htsbuf_append(&q, MSG, sizeof(MSG)-1); htsbuf_qprintf(&q, "USER-AGENT: unix/1.0 UPnP/1.1 TVHeadend/%s\r\n", tvheadend_version); htsbuf_append(&q, "\r\n", 2); upnp_send(&q, NULL, 0); htsbuf_queue_flush(&q); gtimer_arm_ms(&satip_discovery_msearch_timer, satip_discovery_send_msearch, (void *)(intptr_t)(attempt + 1), attempt * 11); #undef MSG }
static int hc_prop(http_connection_t *hc, const char *remain, void *opaque, http_cmd_t method) { htsbuf_queue_t out; rstr_t *r; int rval, i; prop_t *p; const char *action = http_arg_get_req(hc, "action"); if(remain == NULL) return 404; p = prop_from_path(remain); if(p == NULL) return 404; htsbuf_queue_init(&out, 0); switch(method) { case HTTP_CMD_GET: if(action != NULL) { event_t *e = event_create_action_str(action); prop_send_ext_event(p, e); event_release(e); rval = HTTP_STATUS_OK; break; } r = prop_get_string(p, NULL); if(r == NULL) { char **childs = prop_get_name_of_childs(p); if(childs == NULL) { rval = HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE; break; } for(i = 0; childs[i] != NULL; i++) { htsbuf_qprintf(&out, "\t%s\n", childs[i]); } } else { htsbuf_append(&out, rstr_get(r), strlen(rstr_get(r))); htsbuf_append(&out, "\n", 1); rstr_release(r); } rval = http_send_reply(hc, 0, "text/ascii", NULL, NULL, 0, &out); break; default: rval = HTTP_STATUS_METHOD_NOT_ALLOWED; break; } prop_ref_dec(p); return rval; }
static void md_style(htsbuf_queue_t *hq, const char *style, const char *s) { size_t l = strlen(style); htsbuf_append(hq, style, l); htsbuf_append_str(hq, s); htsbuf_append(hq, style, l); }
static int md_nl(htsbuf_queue_t *hq, int nl) { if (nl) htsbuf_append(hq, "\n", 1); return 1; }
/** * List of all classes with documentation */ static int http_markdown_classes(http_connection_t *hc) { idclass_t const **all, **all2; const idclass_t *ic; htsbuf_queue_t *hq = &hc->hc_reply; pthread_mutex_lock(&global_lock); all = idclass_find_all(); if (all == NULL) { pthread_mutex_unlock(&global_lock); return HTTP_STATUS_NOT_FOUND; } for (all2 = all; *all2; all2++) { ic = *all2; if (ic->ic_caption) { htsbuf_append_str(hq, ic->ic_class); htsbuf_append(hq, "\n", 1); } } pthread_mutex_unlock(&global_lock); free(all); return 0; }
static void md_header(htsbuf_queue_t *hq, const char *prefix, const char *s) { htsbuf_append_str(hq, prefix); htsbuf_append_str(hq, s); htsbuf_append(hq, "\n", 1); }
static void screenshot_response_task(void *task) { response_t *r = task; hts_mutex_lock(&screenshot_mutex); if(screenshot_connection == NULL) { hts_mutex_unlock(&screenshot_mutex); } else { http_connection_t *hc = screenshot_connection; screenshot_connection = NULL; hts_mutex_unlock(&screenshot_mutex); if(r->url != NULL) { http_redirect(hc, r->url); } else { const char *msg = r->errmsg; if(msg == NULL) msg = "Error not specified"; htsbuf_queue_t out; htsbuf_queue_init(&out, 0); htsbuf_append(&out, msg, strlen(msg)); htsbuf_append_byte(&out, '\n'); http_send_reply(hc, 500, "text/plain", NULL, NULL, 0, &out); } } free(r->url); free(r->errmsg); free(r); }
static void js_json_emit_jsval(JSContext *cx, jsval value, htsbuf_queue_t *out) { char buf[100]; if(JSVAL_IS_BOOLEAN(value)) { if(JSVAL_TO_BOOLEAN(value)) htsbuf_append(out, "true", 4); else htsbuf_append(out, "false", 5); } else if(JSVAL_IS_INT(value)) { snprintf(buf, sizeof(buf), "%d", JSVAL_TO_INT(value)); htsbuf_append(out, buf, strlen(buf)); } else if(JSVAL_IS_DOUBLE(value)) { double dbl; if(JS_ValueToNumber(cx, value, &dbl) && !my_double2str(buf, sizeof(buf), dbl)) htsbuf_append(out, buf, strlen(buf)); else htsbuf_append(out, "null", 4); } else if(JSVAL_IS_NULL(value)) { htsbuf_append(out, "null", 4); } else if(JSVAL_IS_STRING(value)) { js_json_emit_str(cx, value, out); } else if(JSVAL_IS_OBJECT(value)) { JSObject *obj = JSVAL_TO_OBJECT(value); JSClass *c = JS_GetClass(cx, obj); if(!strcmp(c->name, "XML")) // Treat some classes special js_json_emit_str(cx, value, out); else { if(json_encode_from_object(cx, obj, out)) htsbuf_append(out, "null", 4); } } }
static int os_write(struct tcp_stream *ts, const void *data, int len) { if(!ts->ts_nonblock) return safe_write(ts->ts_fd, data, len); htsbuf_append(&ts->ts_sendq, data, len); os_write_try(ts); return len; }
static void md_text(htsbuf_queue_t *hq, const char *first, const char *next, const char *text) { char *s, *t, *p; int col, nl; t = s = p = tvh_strdupa(text); col = nl = 0; while (*s) { if (++col > 74) { nl = md_nl(hq, nl); if (first) { htsbuf_append_str(hq, first); first = NULL; } else if (next) { htsbuf_append_str(hq, next); } if (p <= t) p = t + 74; htsbuf_append(hq, t, p - t); col = 0; t = s = p; } else if (*s <= ' ') { *s = ' '; p = ++s; } else { s++; } } if (t < s) { md_nl(hq, nl); if (first) htsbuf_append_str(hq, first); else if (next) htsbuf_append_str(hq, next); htsbuf_append(hq, t, s - t); } }
static int hc_image(http_connection_t *hc, const char *remain, void *opaque, http_cmd_t method) { htsbuf_queue_t out; pixmap_t *pm; char errbuf[200]; const char *content; image_meta_t im = {0}; im.im_no_decoding = 1; rstr_t *url = rstr_alloc(remain); pm = backend_imageloader(url, &im, NULL, errbuf, sizeof(errbuf), NULL, NULL, NULL); rstr_release(url); if(pm == NULL) return http_error(hc, 404, "Unable to load image %s : %s", remain, errbuf); if(!pixmap_is_coded(pm)) { pixmap_release(pm); return http_error(hc, 404, "Unable to load image %s : Original data not available", remain); } htsbuf_queue_init(&out, 0); htsbuf_append(&out, pm->pm_data, pm->pm_size); switch(pm->pm_type) { case PIXMAP_JPEG: content = "image/jpeg"; break; case PIXMAP_PNG: content = "image/png"; break; case PIXMAP_GIF: content = "image/gif"; break; default: content = "image"; break; } pixmap_release(pm); return http_send_reply(hc, 0, content, NULL, NULL, 0, &out); }
static int hc_open(http_connection_t *hc, const char *remain, void *opaque, http_cmd_t method) { htsbuf_queue_t out; const char *url = http_arg_get_req(hc, "url"); if(url != NULL) { event_dispatch(event_create_openurl(url, NULL, NULL, NULL, NULL, NULL)); return http_redirect(hc, "/showtime/open"); } htsbuf_queue_init(&out, 0); htsbuf_append(&out, openpage, strlen(openpage)); return http_send_reply(hc, 0, "text/html", NULL, NULL, 0, &out); }
static int ssl_write(struct tcp_stream *ts, const void *data, int len) { if(!ts->ts_nonblock) { int r = SSL_write(ts->ts_ssl, data, len); if(r > 0) return r; errno = EBADMSG; return -1; } htsbuf_append(&ts->ts_sendq, data, len); if(ts->ts_read_status != SSL_ERROR_WANT_WRITE) ssl_write_try(ts); return len; }
static int vimeo_code(http_connection_t *hc, const char *remain, void *opaque) { state_entry_t *vce; static state_entry_t *skel; const char *referer = http_arg_get(&hc->hc_args, "referer"); if(referer == NULL || strcmp(referer, "https://movian.tv/")) return 403; const char *state = http_arg_get(&hc->hc_req_args, "state"); if(state == NULL) return 400; if(skel == NULL) skel = calloc(1, sizeof(state_entry_t)); skel->se_state = (char *)state; pthread_mutex_lock(&state_mutex); vce = RB_FIND(&state_entries, skel, se_entry, state_entry_cmp); pthread_mutex_unlock(&state_mutex); if (vce == NULL) return 404; char *code = vce->se_code; htsmsg_t *msg = htsmsg_create_map(); htsmsg_add_str(msg, "code", code); char *out = htsmsg_json_serialize_to_str(msg, 0); htsbuf_append(&hc->hc_reply, out, strlen(out)); free(out); return http_send_reply(hc, 200, "application/json", NULL, NULL, 0); }
static int hc_prop(http_connection_t *hc, const char *remain, void *opaque, http_cmd_t method) { htsbuf_queue_t out; rstr_t *r; int rval, i; prop_t *p = NULL; char *req = (char *)http_arg_get_req(hc, "requests"); char *request; char *saved; if(req == NULL) return 404; htsbuf_queue_init(&out, 0); switch(method) { case HTTP_CMD_POST: for (request = strtok_r(req, ",", &saved); request; request = strtok_r(NULL, ",", &saved)) { p = prop_from_path(request); if (p == NULL) { htsbuf_qprintf(&out, "error:404"); } else { r = prop_get_string(p, NULL); if(r == NULL) { char **childs = prop_get_name_of_childs(p); if(childs == NULL) { htsbuf_qprintf(&out, "error:404"); } else { htsbuf_qprintf(&out, "dir"); for(i = 0; childs[i] != NULL; i++) { htsbuf_qprintf(&out, "%c%s", i ? ',' : ':', childs[i]); } } } else { htsbuf_qprintf(&out, "value:"); htsbuf_append(&out, rstr_get(r), strlen(rstr_get(r))); rstr_release(r); } } htsbuf_append(&out, "\n", 1); } rval = http_send_reply(hc, 0, "text/ascii", NULL, NULL, 0, &out); break; default: rval = HTTP_STATUS_METHOD_NOT_ALLOWED; break; } prop_ref_dec(p); return rval; }
static int hc_image(http_connection_t *hc, const char *remain, void *opaque, http_cmd_t method) { htsbuf_queue_t out; image_t *img; char errbuf[200]; const char *content; image_meta_t im = {0}; im.im_no_decoding = 1; rstr_t *url; const char *u = http_arg_get_req(hc, "url"); if(u != NULL) { url = rstr_alloc(u); url_deescape(rstr_data(url)); } else { if(remain == NULL) { return 404; } url = rstr_alloc(remain); } img = backend_imageloader(url, &im, NULL, errbuf, sizeof(errbuf), NULL, NULL); rstr_release(url); if(img == NULL) return http_error(hc, 404, "Unable to load image %s : %s", remain, errbuf); const image_component_t *ic = image_find_component(img, IMAGE_CODED); if(ic == NULL) { image_release(img); return http_error(hc, 404, "Unable to load image %s : Original data not available", remain); } const image_component_coded_t *icc = &ic->coded; htsbuf_queue_init(&out, 0); htsbuf_append(&out, buf_cstr(icc->icc_buf), buf_len(icc->icc_buf)); switch(icc->icc_type) { case IMAGE_JPEG: content = "image/jpeg"; break; case IMAGE_PNG: content = "image/png"; break; case IMAGE_GIF: content = "image/gif"; break; default: content = "image"; break; } image_release(img); return http_send_reply(hc, 0, content, NULL, NULL, 0, &out); }
static int md_props(htsbuf_queue_t *hq, htsmsg_t *m, const char *lang, int nl) { htsmsg_t *l, *n, *e, *x; htsmsg_field_t *f, *f2; const char *s; int first = 1, b; l = htsmsg_get_list(m, "props"); HTSMSG_FOREACH(f, l) { n = htsmsg_field_get_map(f); if (!n) continue; if (!htsmsg_get_bool(n, "noui", &b) && b) continue; s = htsmsg_get_str(n, "caption"); if (!s) continue; if (first) { nl = md_nl(hq, nl); htsbuf_append_str(hq, "#### "); htsbuf_append_str(hq, tvh_gettext_lang(lang, N_("Items"))); md_nl(hq, 1); md_nl(hq, 1); htsbuf_append_str(hq, tvh_gettext_lang(lang, N_("The items have the following functions:"))); md_nl(hq, 1); first = 0; } nl = md_nl(hq, nl); md_style(hq, "**", s); if (!htsmsg_get_bool(n, "rdonly", &b) && b) { htsbuf_append(hq, " _", 2); htsbuf_append_str(hq, tvh_gettext_lang(lang, N_("(Read-only)"))); htsbuf_append(hq, "_", 1); } md_nl(hq, 1); s = htsmsg_get_str(n, "description"); if (s) { md_text(hq, ": ", " ", s); md_nl(hq, 1); } s = htsmsg_get_str(n, "doc"); if (s) { htsbuf_append_str(hq, s); md_nl(hq, 1); } if (!htsmsg_get_bool_or_default(n, "doc_nlist", 0)) { e = htsmsg_get_list(n, "enum"); if (e) { HTSMSG_FOREACH(f2, e) { x = htsmsg_field_get_map(f2); if (x) { s = htsmsg_get_str(x, "val"); } else { s = htsmsg_field_get_string(f2); } if (s) { md_nl(hq, 1); htsbuf_append(hq, " * ", 4); md_style(hq, "**", s); } } md_nl(hq, 1); }
static void screenshot_process(void *task) { pixmap_t *pm = task; if(pm == NULL) { screenshot_response(NULL, "Screenshot not supported on this platform"); return; } TRACE(TRACE_DEBUG, "Screenshot", "Processing image %d x %d", pm->pm_width, pm->pm_height); int codecid = AV_CODEC_ID_PNG; if(screenshot_connection) codecid = AV_CODEC_ID_MJPEG; buf_t *b = screenshot_compress(pm, codecid); pixmap_release(pm); if(b == NULL) { screenshot_response(NULL, "Unable to compress image"); return; } if(!screenshot_connection) { char path[512]; char errbuf[512]; snprintf(path, sizeof(path), "%s/screenshot.png", gconf.cache_path); fa_handle_t *fa = fa_open_ex(path, errbuf, sizeof(errbuf), FA_WRITE, NULL); if(fa == NULL) { TRACE(TRACE_ERROR, "SCREENSHOT", "Unable to open %s -- %s", path, errbuf); buf_release(b); return; } fa_write(fa, buf_data(b), buf_len(b)); fa_close(fa); TRACE(TRACE_INFO, "SCREENSHOT", "Written to %s", path); buf_release(b); return; } buf_t *result = NULL; htsbuf_queue_t hq; htsbuf_queue_init(&hq, 0); htsbuf_append(&hq, "image=", 6); htsbuf_append_and_escape_url_len(&hq, buf_cstr(b), buf_len(b)); char errbuf[256]; int ret = http_req("https://api.imgur.com/3/upload", HTTP_FLAGS(FA_CONTENT_ON_ERROR), HTTP_REQUEST_HEADER("Authorization", "Client-ID 7c79b311d4797ed"), HTTP_RESULT_PTR(&result), HTTP_POSTDATA(&hq, "application/x-www-form-urlencoded"), HTTP_ERRBUF(errbuf, sizeof(errbuf)), NULL); if(ret) { screenshot_response(NULL, errbuf); } else { htsmsg_t *response = htsmsg_json_deserialize(buf_cstr(result)); if(response == NULL) { screenshot_response(NULL, "Unable to parse imgur response"); } else { if(htsmsg_get_u32_or_default(response, "success", 0)) { const char *url = htsmsg_get_str_multi(response, "data", "link", NULL); screenshot_response(url, "No link in imgur response"); } else { const char *msg = htsmsg_get_str_multi(response, "data", "error", NULL); if(msg == NULL) { screenshot_response(NULL, "Unkown imgur error"); } else { snprintf(errbuf, sizeof(errbuf), "Imgur error: %s", msg); screenshot_response(NULL, errbuf); } } htsmsg_release(response); } buf_release(result); } buf_release(b); }
static int http_markdown_class(http_connection_t *hc, const char *clazz) { const idclass_t *ic; const char *lang = hc->hc_access->aa_lang_ui; htsbuf_queue_t *hq = &hc->hc_reply; htsmsg_t *m, *l, *n, *e, *x; htsmsg_field_t *f, *f2; const char *s, **doc; int nl = 0, first = 1; pthread_mutex_lock(&global_lock); ic = idclass_find(clazz); if (ic == NULL) { pthread_mutex_unlock(&global_lock); return HTTP_STATUS_NOT_FOUND; } doc = ic->ic_doc; m = idclass_serialize(ic, lang); pthread_mutex_unlock(&global_lock); s = htsmsg_get_str(m, "caption"); if (s) { md_header(hq, "##", s); nl = md_nl(hq, 1); } if (doc) { for (; *doc; doc++) { if (*doc[0] == '\xff') { htsbuf_append_str(hq, tvh_gettext_lang(lang, *doc + 1)); } else { htsbuf_append_str(hq, *doc); } } } l = htsmsg_get_list(m, "props"); HTSMSG_FOREACH(f, l) { n = htsmsg_field_get_map(f); if (!n) continue; s = htsmsg_get_str(n, "caption"); if (!s) continue; if (first) { nl = md_nl(hq, nl); htsbuf_append_str(hq, "####"); htsbuf_append_str(hq, tvh_gettext_lang(lang, N_("Items"))); md_nl(hq, 1); md_nl(hq, 1); htsbuf_append_str(hq, tvh_gettext_lang(lang, N_("The items have the following functions:"))); md_nl(hq, 1); first = 0; } nl = md_nl(hq, nl); md_style(hq, "**", s); md_nl(hq, 1); s = htsmsg_get_str(n, "description"); if (s) { md_text(hq, ": ", " ", s); md_nl(hq, 1); } e = htsmsg_get_list(n, "enum"); if (e) { HTSMSG_FOREACH(f2, e) { x = htsmsg_field_get_map(f2); if (x) { s = htsmsg_get_str(x, "val"); } else { s = htsmsg_field_get_string(f2); } if (s) { md_nl(hq, 1); htsbuf_append(hq, " * ", 4); md_style(hq, "**", s); } } md_nl(hq, 1); }
static void * lirc_thread(void *aux) { char buf[200]; uint64_t ircode; uint32_t repeat; char keyname[100]; int i, r, fd, len, n; htsbuf_queue_t q; struct pollfd fds; event_t *e; fd = lirc_fd; htsbuf_queue_init(&q, 0); fds.fd = fd; fds.events = POLLIN; while(1) { r = poll(&fds, 1, -1); if(r > 0) { if((r = read(fd, buf, sizeof(buf))) < 1) { TRACE(TRACE_ERROR, "lircd", "Read error: %s", strerror(errno)); break; } htsbuf_append(&q, buf, r); } while((len = htsbuf_find(&q, 0xa)) != -1) { if(len >= sizeof(buf) - 1) { TRACE(TRACE_ERROR, "lircd", "Command buffer size exceeded"); goto out; } htsbuf_read(&q, buf, len); buf[len] = 0; while(len > 0 && buf[len - 1] < 32) buf[--len] = 0; htsbuf_drop(&q, 1); /* Drop the \n */ n = sscanf(buf, "%"PRIx64" %x %s", &ircode, &repeat, keyname); if(n != 3) { TRACE(TRACE_INFO, "lircd", "Invalid LIRC input: \"%s\"", buf); continue; } if(keyname[0] && keyname[1] == 0) { /* ASCII input */ e = event_create_int(EVENT_UNICODE, keyname[0]); } else { e = NULL; for(i = 0; i < sizeof(lircmap) / sizeof(lircmap[0]); i++) { if(!strcasecmp(keyname, lircmap[i].name)) { action_type_t av[3] = { lircmap[i].action1, lircmap[i].action2, }; if(av[1] != ACTION_NONE) e = event_create_action_multi(av, 2); else e = event_create_action_multi(av, 1); break; } } } if(e == NULL) { snprintf(buf, sizeof(buf), "IR+%s", keyname); e = event_create_str(EVENT_KEYDESC, buf); } event_to_ui(e); } } out: close(fd); htsbuf_queue_flush(&q); return NULL; }
static int json_encode_from_object(JSContext *cx, JSObject *obj, htsbuf_queue_t *out) { int objtype = 0; JSIdArray *ida; int i; const char *n; if((ida = JS_Enumerate(cx, obj)) == NULL) return -1; for(i = 0; i < ida->length; i++) { jsval name, value; if(!JS_IdToValue(cx, ida->vector[i], &name)) continue; if(JSVAL_IS_STRING(name)) { JSString *str = JSVAL_TO_STRING(name); n = JS_GetStringBytes(str); if(!JS_GetProperty(cx, obj, n, &value)) continue; if(objtype == 0) { htsbuf_append(out, "{", 1); objtype = OBJTYPE_MAP; } else if(objtype != OBJTYPE_MAP) continue; else htsbuf_append(out, ",", 1); htsbuf_append_and_escape_jsonstr(out, n); htsbuf_append(out, ":", 1); } else if(JSVAL_IS_INT(name)) { if(!JS_GetElement(cx, obj, JSVAL_TO_INT(name), &value) || JSVAL_IS_VOID(value)) continue; if(objtype == 0) { htsbuf_append(out, "[", 1); objtype = OBJTYPE_LIST; } else if(objtype != OBJTYPE_LIST) continue; else htsbuf_append(out, ",", 1); } else { continue; } js_json_emit_jsval(cx, value, out); } JS_DestroyIdArray(cx, ida); switch(objtype) { case OBJTYPE_LIST: htsbuf_append(out, "]", 1); break; case OBJTYPE_MAP: htsbuf_append(out, "}", 1); break; default: return -1; } return 0; }