コード例 #1
0
ファイル: nicodown.c プロジェクト: mattn/nicodown
int
main(int argc, char* argv[]) {
    CURL* curl = NULL;
    CURLcode res;
    int status = 0;
    char error[CURL_ERROR_SIZE];
    char fname[256];
    char line[256];
    char cookie[2048];
    char query[2048];
    char* buf = NULL;
    char* ptr = NULL;
    char* tmp = NULL;
    char* id;
    FILE* fp = NULL;
    MEMFILE* mf; // mem file
    MEMFILE* hf; // mem file for header

    // usage
    if (argc != 2) {
        fputs("usage: nicodown [video_id]\n", stderr);
        goto leave;
    }

    if (!(ptr = getenv("HOME"))) {
#ifdef _WIN32
        if (!(ptr = getenv("USERPROFILE"))) {
            fprintf(stderr, "failed to get HOME directory\n");
            goto leave;
        }
#else
        fprintf(stderr, "failed to get HOME directory\n");
        goto leave;
#endif
    }

    sprintf(fname, "%s/.nicodownrc", ptr);
    fp = fopen(fname, "r");
    if (!fp || !fgets(line, sizeof(line), fp)) {
        if (fp) fclose(fp);
        fprintf(stderr, "failed to read ~/.nicodownrc\n");
        goto leave;
    }
    fclose(fp);
    tmp = strpbrk(line, "\r\n");
    if (tmp) *tmp = 0;
    ptr = strchr(line, ':');
    if (!ptr) {
        fprintf(stderr, "failed to parse ~/.nicodownrc\n");
        goto leave;
    }
    *ptr++ = 0;

    id = argv[1];
    if (!strncmp(id, WATCH_URL_BASE, strlen(WATCH_URL_BASE))) {
        id += strlen(WATCH_URL_BASE);
    }
    sprintf(query, "mail_tel=%s&password=%s&next_url=/watch/%s", line, ptr, id);

    // default filename
    sprintf(fname, "%s.flv", id);

    curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite);

    // login
    mf = memfopen();
    hf = memfopen();
    curl_easy_setopt(curl, CURLOPT_URL, "https://secure.nicovideo.jp/secure/login?site=niconico");
    curl_easy_setopt(curl, CURLOPT_POST, 1);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, query);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf);
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, hf);
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, memfwrite);
    res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        fputs(error, stderr);
        memfclose(mf);
        memfclose(hf);
        goto leave;
    }

    // parse cookie
    memset(cookie, 0, sizeof(cookie));
    buf = memfstrdup(hf);
    tmp = buf;
    while (tmp = strstr(tmp, "Set-Cookie: ")) {
        ptr = tmp;
        tmp = strpbrk(ptr, "\r\n;");
        if (tmp) *tmp = 0;
        strcpy(cookie, ptr + 12);
        if (strncmp(cookie, "user_session=user", 17) == 0) {
            curl_easy_setopt(curl, CURLOPT_COOKIE, cookie);
            break;
        }
        tmp++;
    }
    free(buf);

    memfclose(mf);
    memfclose(hf);
    mf = memfopen();
    hf = memfopen();
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf);
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, hf);

    // redirect
    sprintf(query, "http://www.nicovideo.jp/watch/%s", id);
    curl_easy_setopt(curl, CURLOPT_URL, query);
    curl_easy_setopt(curl, CURLOPT_POST, 0);
    res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        fputs(error, stderr);
        memfclose(mf);
        memfclose(hf);
        goto leave;
    }

    // parse cookie
    buf = memfstrdup(hf);
    ptr = NULL;
    tmp = buf;
    while (tmp = strstr(tmp, "Set-Cookie: ")) {
        ptr = tmp + 12;
        tmp = strpbrk(ptr, "\r\n;");
        if (tmp) *tmp = 0;
        strcat(cookie, ";");
        strcat(cookie, ptr);
        tmp++;
    }
    curl_easy_setopt(curl, CURLOPT_COOKIE, cookie);
    free(buf);

    // parse response body
    buf = memfstrdup(mf);
    if (buf && strstr(buf, "id=\"login_bar\"")) {
        free(buf);
        fprintf(stderr, "failed to login\n");
        memfclose(mf);
        memfclose(hf);
        goto leave;
    }
    free(buf);
    memfclose(hf);
    memfclose(mf);

    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL);
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, NULL);
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, NULL);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);

    // get video query, and get filename
    sprintf(query, "http://www.nicovideo.jp/api/getthumbinfo?v=%s", id);
    mf = memfopen();
    curl_easy_setopt(curl, CURLOPT_URL, query);
    curl_easy_setopt(curl, CURLOPT_POST, 0);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite);
    res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        fputs(error, stderr);
        memfclose(mf);
        goto leave;
    }

#ifdef USE_LIBXML
    // parse title node
    {
        xmlDocPtr doc = NULL;
        xmlXPathContextPtr xpathctx;
        xmlXPathObjectPtr xpathobj;

        doc = xmlParseMemory(mf->data, mf->size);
        xpathctx = doc ? xmlXPathNewContext(doc) : NULL;
        xpathobj = xpathctx ? xmlXPathEvalExpression((xmlChar*) "//title", xpathctx) : NULL;
        if (xpathobj) {
            int n;
            xmlNodeSetPtr nodes = xpathobj->nodesetval;
            for(n = 0; nodes && n < xmlXPathNodeSetGetLength(nodes); n++) {
                xmlNodePtr node = nodes->nodeTab[n];
                if(node->type != XML_ATTRIBUTE_NODE && node->type != XML_ELEMENT_NODE && node->type != XML_CDATA_SECTION_NODE) continue;
                if (node->type == XML_CDATA_SECTION_NODE)
                    sprintf(fname, "%s.flv", (char*) node->content);
                else
                    if (node->children)
                        sprintf(fname, "%s.flv", (char*) node->children->content);
                break;
            }
        }
        if (xpathobj ) xmlXPathFreeObject(xpathobj);
        if (xpathctx) xmlXPathFreeContext(xpathctx);
        if (doc) xmlFreeDoc(doc);
    }
#else
    buf = memfstrdup(mf);
    ptr = buf ? strstr(buf, "<title>") : NULL;
    if (ptr) {
        ptr += 7;
        tmp = strstr(ptr, "</title>");
        if (*tmp) {
            *tmp = 0;
            sprintf(fname, "%s.flv", ptr);
        }
    }
    if (buf) free(buf);
#endif

    memfclose(mf);

#ifdef _WIN32
    {
        UINT codePage;
        size_t wcssize;
        wchar_t* wcsstr;
        size_t mbssize;
        char* mbsstr;

        codePage = CP_UTF8;
        wcssize = MultiByteToWideChar(codePage, 0, fname, -1,  NULL, 0);
        wcsstr = (wchar_t*) malloc(sizeof(wchar_t) * (wcssize + 1));
        wcssize = MultiByteToWideChar(codePage, 0, fname, -1, wcsstr, wcssize + 1);
        wcsstr[wcssize] = 0;
        codePage = GetACP();
        mbssize = WideCharToMultiByte(codePage, 0, (LPCWSTR) wcsstr,-1,NULL,0,NULL,NULL);
        mbsstr = (char*) malloc(mbssize+1);
        mbssize = WideCharToMultiByte(codePage, 0, (LPCWSTR) wcsstr, -1, mbsstr, mbssize, NULL, NULL);
        mbsstr[mbssize] = 0;
        strcpy(fname, mbsstr);
        free(mbsstr);
        free(wcsstr);
    }
#endif

    // get video query
    sprintf(query, "http://flapi.nicovideo.jp/api/getflv?v=%s", id);
    mf = memfopen();
    curl_easy_setopt(curl, CURLOPT_URL, query);
    curl_easy_setopt(curl, CURLOPT_POST, 0);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite);
    res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        fputs(error, stderr);
        memfclose(mf);
        goto leave;
    }
    buf = memfstrdup(mf);
    ptr = strstr(buf, "url=");
    if (!ptr) {
        if (buf) free(buf);
        fprintf(stderr, "failed to get video info\n");
        memfclose(mf);
        goto leave;
    }
    tmp = strstr(ptr, "&");
    if (tmp) *tmp = 0;
    tmp = ptr;
    while(*tmp) {
        if (IS_QUOTED(tmp)) {
            unsigned int num = 0;
            sscanf(tmp+1, "%02x", &num);
            *tmp = (char)num;
            strcpy(tmp + 1, tmp + 3);
        }
        tmp++;
    }
    strcpy(query, ptr + 4);
    printf("URL: %s\n", query);
    free(buf);
    memfclose(mf);

    // sanitize filename
    ptr = fname;
    while (*ptr) {
        if (strchr("\\/|:<>\"?*", *ptr)) *ptr = '_';
        ptr++;
    }

    // download video
    fp = fopen(fname, "wb");
    if (!fp) {
        fprintf(stderr, "failed to open file: %s\n", fname);
        goto leave;
    }
    curl_easy_setopt(curl, CURLOPT_URL, query);
    curl_easy_setopt(curl, CURLOPT_POST, 0);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress);
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, (void*)fname);
    curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, BUFSIZ);
    /* curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); */
    res = curl_easy_perform(curl);
    fclose(fp);
    if (res != CURLE_OK) {
        fputs(error, stderr);
        goto leave;
    }

leave:
    if (curl) curl_easy_cleanup(curl);
    printf("\n");

    return 0;
}
コード例 #2
0
ファイル: c-specialize.c プロジェクト: hostilefork/rebol
//
//  Specialize_Action_Throws: C
//
// Create a new ACTION! value that uses the same implementation as another,
// but just takes fewer arguments or refinements.  It does this by storing a
// heap-based "exemplar" FRAME! in the specialized action; this stores the
// values to preload in the stack frame cells when it is invoked.
//
// The caller may provide information on the order in which refinements are
// to be specialized, using the data stack.  These refinements should be
// pushed in the *reverse* order of their invocation, so append/dup/part
// has /DUP at DS_TOP, and /PART under it.  List stops at lowest_ordered_dsp.
//
bool Specialize_Action_Throws(
    REBVAL *out,
    REBVAL *specializee,
    REBSTR *opt_specializee_name,
    REBVAL *opt_def, // !!! REVIEW: binding modified directly (not copied)
    REBDSP lowest_ordered_dsp
){
    assert(out != specializee);

    struct Reb_Binder binder;
    if (opt_def)
        INIT_BINDER(&binder);

    REBACT *unspecialized = VAL_ACTION(specializee);

    // This produces a context where partially specialized refinement slots
    // will be on the stack (including any we are adding "virtually", from
    // the current DSP down to the lowest_ordered_dsp).
    //
    REBCTX *exemplar = Make_Context_For_Action_Push_Partials(
        specializee,
        lowest_ordered_dsp,
        opt_def ? &binder : nullptr,
        CELL_MASK_NON_STACK
    );
    Manage_Array(CTX_VARLIST(exemplar)); // destined to be managed, guarded

    if (opt_def) { // code that fills the frame...fully or partially
        //
        // Bind all the SET-WORD! in the body that match params in the frame
        // into the frame.  This means `value: value` can very likely have
        // `value:` bound for assignments into the frame while `value` refers
        // to whatever value was in the context the specialization is running
        // in, but this is likely the more useful behavior.
        //
        // !!! This binds the actual arg data, not a copy of it--following
        // OBJECT!'s lead.  However, ordinary functions make a copy of the
        // body they are passed before rebinding.  Rethink.

        // See Bind_Values_Core() for explanations of how the binding works.

        Bind_Values_Inner_Loop(
            &binder,
            VAL_ARRAY_AT(opt_def),
            exemplar,
            FLAGIT_KIND(REB_SET_WORD), // types to bind (just set-word!)
            0, // types to "add midstream" to binding as we go (nothing)
            BIND_DEEP
        );

        // !!! Only one binder can be in effect, and we're calling arbitrary
        // code.  Must clean up now vs. in loop we do at the end.  :-(
        //
        RELVAL *key = CTX_KEYS_HEAD(exemplar);
        REBVAL *var = CTX_VARS_HEAD(exemplar);
        for (; NOT_END(key); ++key, ++var) {
            if (Is_Param_Unbindable(key))
                continue; // !!! is this flag still relevant?
            if (Is_Param_Hidden(key)) {
                assert(GET_CELL_FLAG(var, ARG_MARKED_CHECKED));
                continue;
            }
            if (GET_CELL_FLAG(var, ARG_MARKED_CHECKED))
                continue; // may be refinement from stack, now specialized out
            Remove_Binder_Index(&binder, VAL_KEY_CANON(key));
        }
        SHUTDOWN_BINDER(&binder);

        // Run block and ignore result (unless it is thrown)
        //
        PUSH_GC_GUARD(exemplar);
        bool threw = Do_Any_Array_At_Throws(out, opt_def, SPECIFIED);
        DROP_GC_GUARD(exemplar);

        if (threw) {
            DS_DROP_TO(lowest_ordered_dsp);
            return true;
        }
    }

    REBVAL *rootkey = CTX_ROOTKEY(exemplar);

    // Build up the paramlist for the specialized function on the stack.
    // The same walk used for that is used to link and process REB_X_PARTIAL
    // arguments for whether they become fully specialized or not.

    REBDSP dsp_paramlist = DSP;
    Move_Value(DS_PUSH(), ACT_ARCHETYPE(unspecialized));

    REBVAL *param = rootkey + 1;
    REBVAL *arg = CTX_VARS_HEAD(exemplar);

    REBDSP ordered_dsp = lowest_ordered_dsp;

    for (; NOT_END(param); ++param, ++arg) {
        if (TYPE_CHECK(param, REB_TS_REFINEMENT)) {
            if (IS_NULLED(arg)) {
                //
                // A refinement that is nulled is a candidate for usage at the
                // callsite.  Hence it must be pre-empted by our ordered
                // overrides.  -but- the overrides only apply if their slot
                // wasn't filled by the user code.  Yet these values we are
                // putting in disrupt that detection (!), so use another
                // flag (PUSH_PARTIAL) to reflect this state.
                //
                while (ordered_dsp != dsp_paramlist) {
                    ++ordered_dsp;
                    REBVAL *ordered = DS_AT(ordered_dsp);

                    if (not IS_WORD_BOUND(ordered))  // specialize 'print/asdf
                        fail (Error_Bad_Refine_Raw(ordered));

                    REBVAL *slot = CTX_VAR(exemplar, VAL_WORD_INDEX(ordered));
                    if (
                        IS_NULLED(slot) or GET_CELL_FLAG(slot, PUSH_PARTIAL)
                    ){
                        // It's still partial, so set up the pre-empt.
                        //
                        Init_Any_Word_Bound(
                            arg,
                            REB_SYM_WORD,
                            VAL_STORED_CANON(ordered),
                            exemplar,
                            VAL_WORD_INDEX(ordered)
                        );
                        SET_CELL_FLAG(arg, PUSH_PARTIAL);
                        goto unspecialized_arg;
                    }
                    // Otherwise the user filled it in, so skip to next...
                }

                goto unspecialized_arg;  // ran out...no pre-empt needed
            }

            if (GET_CELL_FLAG(arg, ARG_MARKED_CHECKED)) {
                assert(
                    IS_BLANK(arg)
                    or (
                        IS_REFINEMENT(arg)
                        and (
                            VAL_REFINEMENT_SPELLING(arg)
                            == VAL_PARAM_SPELLING(param)
                        )
                    )
                );
            }
            else
                Typecheck_Refinement_And_Canonize(param, arg);

            goto specialized_arg_no_typecheck;
        }

        switch (VAL_PARAM_CLASS(param)) {
          case REB_P_RETURN:
          case REB_P_LOCAL:
            assert(IS_NULLED(arg)); // no bindings, you can't set these
            goto unspecialized_arg;

          default:
            break;
        }

        // It's an argument, either a normal one or a refinement arg.

        if (not IS_NULLED(arg))
            goto specialized_arg_with_check;

    unspecialized_arg:

        assert(NOT_CELL_FLAG(arg, ARG_MARKED_CHECKED));
        assert(
            IS_NULLED(arg)
            or (IS_SYM_WORD(arg) and TYPE_CHECK(param, REB_TS_REFINEMENT))
        );
        Move_Value(DS_PUSH(), param);
        continue;

    specialized_arg_with_check:

        // !!! If argument was previously specialized, should have been type
        // checked already... don't type check again (?)
        //
        if (Is_Param_Variadic(param))
            fail ("Cannot currently SPECIALIZE variadic arguments.");

        if (TYPE_CHECK(param, REB_TS_DEQUOTE_REQUOTE) and IS_QUOTED(arg)) {
            //
            // Have to leave the quotes on, but still want to type check.

            if (not TYPE_CHECK(param, CELL_KIND(VAL_UNESCAPED(arg))))
                fail (arg); // !!! merge w/Error_Invalid_Arg()
        }
        else if (not TYPE_CHECK(param, VAL_TYPE(arg)))
            fail (arg); // !!! merge w/Error_Invalid_Arg()

       SET_CELL_FLAG(arg, ARG_MARKED_CHECKED);

    specialized_arg_no_typecheck:

        // Specialized-out arguments must still be in the parameter list,
        // for enumeration in the evaluator to line up with the frame values
        // of the underlying function.

        assert(GET_CELL_FLAG(arg, ARG_MARKED_CHECKED));
        Move_Value(DS_PUSH(), param);
        TYPE_SET(DS_TOP, REB_TS_HIDDEN);
        continue;
    }

    REBARR *paramlist = Pop_Stack_Values_Core(
        dsp_paramlist,
        SERIES_MASK_PARAMLIST
            | (SER(unspecialized)->header.bits & PARAMLIST_MASK_INHERIT)
    );
    Manage_Array(paramlist);
    RELVAL *rootparam = ARR_HEAD(paramlist);
    VAL_ACT_PARAMLIST_NODE(rootparam) = NOD(paramlist);

    // Everything should have balanced out for a valid specialization
    //
    while (ordered_dsp != DSP) {
        ++ordered_dsp;
        REBVAL *ordered = DS_AT(ordered_dsp);
        if (not IS_WORD_BOUND(ordered))  // specialize 'print/asdf
            fail (Error_Bad_Refine_Raw(ordered));

        REBVAL *slot = CTX_VAR(exemplar, VAL_WORD_INDEX(ordered));
        assert(not IS_NULLED(slot) and NOT_CELL_FLAG(slot, PUSH_PARTIAL));
        UNUSED(slot);
    }
    DS_DROP_TO(lowest_ordered_dsp);

    // See %sysobj.r for `specialized-meta:` object template

    REBVAL *example = Get_System(SYS_STANDARD, STD_SPECIALIZED_META);

    REBCTX *meta = Copy_Context_Shallow_Managed(VAL_CONTEXT(example));

    Init_Nulled(CTX_VAR(meta, STD_SPECIALIZED_META_DESCRIPTION)); // default
    Move_Value(
        CTX_VAR(meta, STD_SPECIALIZED_META_SPECIALIZEE),
        specializee
    );
    if (not opt_specializee_name)
        Init_Nulled(CTX_VAR(meta, STD_SPECIALIZED_META_SPECIALIZEE_NAME));
    else
        Init_Word(
            CTX_VAR(meta, STD_SPECIALIZED_META_SPECIALIZEE_NAME),
            opt_specializee_name
        );

    MISC_META_NODE(paramlist) = NOD(meta);

    REBACT *specialized = Make_Action(
        paramlist,
        &Specializer_Dispatcher,
        ACT_UNDERLYING(unspecialized), // same underlying action as this
        exemplar, // also provide a context of specialization values
        1 // details array capacity
    );
    assert(CTX_KEYLIST(exemplar) == ACT_PARAMLIST(unspecialized));

    assert(
        GET_ACTION_FLAG(specialized, IS_INVISIBLE)
        == GET_ACTION_FLAG(unspecialized, IS_INVISIBLE)
    );

    // The "body" is the FRAME! value of the specialization.  It takes on the
    // binding we want to use (which we can't put in the exemplar archetype,
    // that binding has to be UNBOUND).  It also remembers the original
    // action in the phase, so Specializer_Dispatcher() knows what to call.
    //
    RELVAL *body = ARR_HEAD(ACT_DETAILS(specialized));
    Move_Value(body, CTX_ARCHETYPE(exemplar));
    INIT_BINDING(body, VAL_BINDING(specializee));
    INIT_VAL_CONTEXT_PHASE(body, unspecialized);

    Init_Action_Unbound(out, specialized);
    return false; // code block did not throw
}