Właśnie opublikowałem jedno z dziwniejszych przeżyć programistycznych. Niby jest to stare dobre C… Jednak sami powiedzcie, jak to Wam wygląda.

Plik poniższy wprowadza do Fossila możliwość rejestracji użytkownika przez HTTP.

/*
** Copyright (c) 2010 Remigiusz Modrzejewski 
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)

** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
**   Firstname.Lastname@gmail.com
**   <http://lrem.net/>
**
*******************************************************************************
**
** This file contains code for generating the register screen.
**
*/
#include "config.h"
#include "register.h"
#if defined(_WIN32)  
#  include <windows.h>           /* for Sleep */
#  if defined(__MINGW32__) || defined(_MSC_VER)
#    define sleep Sleep            /* windows does not have sleep, but Sleep */
#  endif
#endif
#include <time.h>

/*
** WEBPAGE: register
**
** Generate the register page.
**
*/
void register_page(void){
  const char *zUsername, *zPasswd, *zConfirm, *zContact, *zCS, *zPw, *zCap;

  style_header("Register");
  zUsername = P("u");
  zPasswd = P("p");
  zConfirm = P("cp");
  zContact = P("c");
  zCap = P("cap");
  zCS = P("cs"); /* Captcha Secret */

  /* Try to make any sense from user input. */
  if( P("new") ){
    zPw = captcha_decode((unsigned int)atoi(zCS)); /* If zCS == null this will
                                                      kill the page, what is in
                                                      fact good, as the request
                                                      is forged.*/
    if( !(zUsername && zPasswd && zConfirm && zContact) ){
      @ <p><span class="loginError">
      @ All fields are obligatory.
      @ </span></p>
    }else if( strcmp(zPasswd,zConfirm)!=0 ){
      @ <p><span class="loginError">
      @ The two copies of your new passwords do not match.
      @ </span></p>
    }else if( strcasecmp(zPw, zCap)!=0 ){
      @ <p><span class="loginError">
      @ Captcha text invalid.
      @ </span></p>
    }else{
      /* This almost is stupid copy-paste of code from user.c:user_cmd(). Meh. */
      Blob passwd, login, contact;

      blob_init(&login, zUsername, -1);
      blob_init(&contact, zContact, -1);
      blob_init(&passwd, zPasswd, -1);

      if( db_exists("SELECT 1 FROM user WHERE login=%B", &login) ){
        /* Here lies the reason I don't use zErrMsg - it would not substitute
         * this %s(zUsername), or at least I don't know how to force it to.*/
        @ <p><span class="loginError">
        @ %s(zUsername) already exists.
        @ </span></p>
      }else{
        char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
        db_multi_exec(
            "INSERT INTO user(login,pw,cap,info)"
            "VALUES(%B,%Q,'u',%B)", /* u - register as reader, not developer! */
            &login, zPw, &contact
            );
        free(zPw);

        /* The user is registered, now just log him in. */
        int uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUsername);
        char *zCookie;
        const char *zCookieName = login_cookie_name();
        const char *zExpire = db_get("cookie-expire","8766");
        int expires = atoi(zExpire)*3600;
        const char *zIpAddr = PD("REMOTE_ADDR","nil");

        zCookie = db_text(0, "SELECT '%d/' || hex(randomblob(25))", uid);
        cgi_set_cookie(zCookieName, zCookie, 0, expires);
        db_multi_exec(
            "UPDATE user SET cookie=%Q, ipaddr=%Q, "
            "  cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
            zCookie, zIpAddr, expires, uid
            );
        redirect_to_g();

      }
    }
  }

  /* Prepare the captcha. */
  unsigned int uSeed = captcha_seed();
  char const *zDecoded = captcha_decode(uSeed);
  char *zCaptcha = captcha_render(zDecoded);

  /* Print out the registration form. */
  @ <form action="register" method="post">
  if( P("g") ){
    @ <input type="hidden" name="g" value="%h(P("g"))" />
  }
  @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
  @ <table class="login_out">
  @ <tr>
  @   <td class="login_out_label">User ID:</td>
  @   <td><input type="text" id="u" name="u" value="" size="30" /></td>
  @ </tr>
  @ <tr>
  @   <td class="login_out_label">Password:</td>
  @   <td><input type="password" id="p" name="p" value="" size="30" /></td>
  @ </tr>
  @ <tr>
  @   <td class="login_out_label">Confirm password:</td>
  @   <td><input type="password" id="cp" name="cp" value="" size="30" /></td>
  @ </tr>
  @ <tr>
  @   <td class="login_out_label">Contact info:</td>
  @   <td><input type="text" id="c" name="c" value="" size="30" /></td>
  @ </tr>
  @ <tr>
  @   <td class="login_out_label">Captcha text (below):</td>
  @   <td><input type="text" id="cap" name="cap" value="" size="30" /></td>
  @ </tr>
  @ <tr><td></td>
  @ <td><input type="submit" name="new" value="Register" /></td></tr>
  @ </table>
  @ <div class="captcha"><table class="captcha"><tr><td><pre>
  @ %s(zCaptcha)
  @ </pre></td></tr></table>
  @ </form>
    ;
  style_footer();

  free(zCaptcha);
}

// vim: set ts=2 sw=2 sts=2 et :

Komentarze

  • airborn (2010-12-05 17:31:47): prawie jak bym widział swoją magisterkę…
  • Remigiusz ‘lRem’ Modrzejewski (2010-12-05 17:37:15): Współczuję… Ja do swojej kodowałem w Pythonie, acz i to było małym dodatkiem do teorii.
  • Bartek (2010-12-06 14:46:56): W C jest operator @ ? co on robi?
  • Remigiusz ‘lRem’ Modrzejewski (2010-12-06 14:48:22): To jest magia preprocessingu. W którymś momencie procesu kompilacji jest to tłumaczone na coś printf-o-podobnego wysyłającego do klienta html.
  • pecet (2010-12-06 21:51:31): Taki postprocessing tylko zaciemnia kod. C lubię za prostotę i dość dużą kontrolę nad tym co się dzieje, to jest niestety przeciwieństwem tego :/
  • lRem (2010-12-07 13:32:17): No sam nie wiem. Co jest jaśniejsze. Wersja z tym dzikim preprocesingiem: @ <input type="hidden” name="g” value=”%h(P(“g”))” /> Czy bez niego:
  • SebaS86 (2010-12-07 18:44:50): Ale to chyba jakiś zewnętrzny preprocesor?
  • pecet (2010-12-07 20:32:55): lrem – pomijając konieczność escapowania " " " to wydaje się całkiem czytelna wersja z printf IMHO
  • Remigiusz ‘lRem’ Modrzejewski (2010-12-08 15:38:56): SebaS86: ta, to jest w ogóle dziki makesystem gdzie make wywołuje skrypt w TCL-u który tworzy następny Makefile, wszystkie źródła są przemielone przez dodatkowy preprocesor, który również jest kompilowany w międzyczasie, na koniec nawet tworzy toto paczki. Część plików .c tworzonych jest podczas budowania na podstawie informacji zaszytych w komentarzach innych plików. Jak dodasz do tego jeszcze natywną obsługę cross-compile, to wyobrazisz sobie z jakim monstrum ma się do czynienia chcąc cokolwiek dodać.