письмо по SMTP из консоли. Delphi.

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by ErrorNeo, 3 Aug 2009.

  1. ErrorNeo

    ErrorNeo Elder - Старейшина

    Joined:
    2 May 2009
    Messages:
    923
    Likes Received:
    836
    Reputations:
    402
    сабж.

    требуется отправить письмо на ящик reciever@yandex.ru
    сожержащее в body кое-какие данные.
    "отпраивтель" - без разницы, главное - содержание письма и его доставка по назначению.
    smtp ya.ru: 213.180.204.3 , port: 25.

    нарыл код, который вроде реализует через сокеты (не работал с ними раньше)
    (нарыл здась: /showthread.php?p=671692)

    после небольшой редакции компилица, трассируеца... сниффаеца.
    Code:
    TCP	
    192.168.0.100	2652
    205.188.12.134	5190				
    132 байт
    и, соответственно, нифига не работает так, как должен =\

    код:

    Code:
    program Project1;
    
    uses
      SysUtils,
      Winsock,
      Windows;
    
    {
    smtp - ip адрес smtp сервера
    port - порт smtp сервера, по умолчанию 25
    from - адрес отправителя
    dest - адрес получателя
    subject - тема письма
    body - текст писма
    Возвращает True если письмо было успешно отправленно...
    }
    function mail(smtp: string; port: integer; from, dest, subject, body: string): bool;
    const
      cl = #13#10;
    var
    WSAData: TWSAData;
      Host: TSockAddrIn;
      Sock: TSocket;
      res: Integer;
      buff: array[1..255] of Char;
    
      { отправляем данные через сокет }
      procedure senddata(str: string);
      var
        i: integer;
      begin
        for i := 1 to Length(str) do
          if send(Sock, str[i], 1, 0) = SOCKET_ERROR then
            exit;
      end;
      { получаем ответ от команды }
      function recvdata(accept: string): bool;
      var
        buff: array[1..255] of Char;
      begin
        res := recv(Sock, buff, SizeOf(buff), 0);
        Result := ((Res = SOCKET_ERROR) or (Copy(buff, 1, 3) = accept));
      end;
    begin
      try
        result := false;
        { инициализация сокета }
        WSAStartUp(257, WSAData);
        Sock := socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
        if Sock = INVALID_SOCKET then
          Exit;
        { устанавливаем хост и порт сервера }
        res := inet_addr(PChar(smtp));
        if res <= 0 then
          exit;
        Host.sin_family := AF_INET;
        Host.sin_port := htons(port);
        Host.sin_addr.S_addr := res;
        { подключаемся к серверу }
        if connect(Sock, Host, SizeOf(Host)) > 0 then
          Exit;
        { приветствие сервера }
        if not recvdata('220') then
          Exit;
        { EHLO }
        senddata('EHLO' + cl);
        if not recvdata('250') then
          Exit;
        { MAIL FROM: }
        senddata('MAIL FROM:' + from + cl);
        if not recvdata('250') then
          Exit;
        { RCPT TO: }
        senddata('RCPT TO:' + dest + cl);
        if not recvdata('250') then
          Exit;
        { DATA }
        senddata('DATA' + cl);
        if not recvdata('354') then
          Exit;
        { отправляем текст сообщения }
        senddata('Subject:' + subject + cl + cl + body + cl + '.');
        if not recvdata('250') then
          Exit;
        { отключаемся от сервера }
        senddata('QUIT' + cl);
        result := true;
      finally
        { убиваем сокет }
        closesocket(sock);
        WSACleanup;
      end;
    end;
    
    //----------------------------------------------
    
    begin
    
       mail('213.180.204.3',25,'reciever@yandex.ru' ,'admin@company.mail', 'subj', 'body text');
    
    end.
    
    сабж мне как бэ нужен, так что разбирусь и сам..
    но за любую помощь буду признателен.
    Как по данному коду, так и по любому другому, _рабочему_ методу отправки e-mail на заранее заданный адрес на заранее заданном почтовике, содержащему в body (или как альтернатива - аттачменте) те или иные "данные" (текстовый файл)
     
    #1 ErrorNeo, 3 Aug 2009
    Last edited: 3 Aug 2009
  2. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    Любое письмо так посланное будет скорее всего в спаме.
    К таму же тебе нужно не на абы какие адреса слать письма,
    а именно на MX шлюзы(потому что тока они принимают письма без авторизации и с левых адресов)

    Когдато давно писал примитивную тестовую версию для этих целей

    Code:
    program sm;
    
    {$APPTYPE CONSOLE}
    
    uses
      Windows,WinDNS,Winsock;
    
    function MXResolve(Domain: PChar): string;
    var
    pQueryResultsSet: PDNS_RECORDA;
    Name: PChar;
    begin
    pQueryResultsSet := nil;
    if DnsQuery_A(Domain, DNS_TYPE_MX, DNS_QUERY_STANDARD, nil, @pQueryResultsSet, nil) = 0 then
     begin
      Result:=pQueryResultsSet^.Data.MX.pNameExchange;
      GlobalFree(dword(pQueryResultsSet));
     end;
    end;
    
    function GetIPAddress(name: string): string;
    var
      p: PHostEnt;
    begin
    p:=GetHostByName(PChar(name));
    if p=nil then result:=name else
    result:=inet_ntoa(PInAddr(p.h_addr_list^)^);
    end;
    
    
    var
    buf:array[0..1023] of char;
    s:string;
    f:textfile;
    c:char;
    mail_from,mail_to,mail_file:string;
    MX_SERVER:string;
    WSData:TWSAData;
    so:thandle;
    ca:sockaddr_in;
    begin
    writeln('SendMail (C) SLESH (ICQ: 266-334-734)');
    writeln('Usage: sm.exe MAIL_FROM MAIL_TO MAIL_FILE');
    
    
    mail_from:=paramstr(1);
    mail_to:=paramstr(2);
    mail_file:=paramstr(3);
    
    if (mail_from='') or (mail_to='') or (mail_file='') then exit;
    
    MX_SERVER:=MAIL_TO;
    delete(MX_SERVER,1,pos('@',MX_SERVER));
    write('[*] WSAStartup...');
    if WSAStartup($101, WSData)=-1 then begin write('ERROR');exit;end;
    writeln('OK');
    
    write('[*] RESOLVE MX SERVER...');
    MX_SERVER:=MXResolve(Pansichar(MX_SERVER));
    if MX_SERVER='' then
     begin
      write('ERROR');
      exit;
     end;
    writeln('OK');
    write('[*] RESOLVE IP MX SERVER...');
    MX_SERVER:=GetIPAddress(MX_SERVER);
    if MX_SERVER='' then
     begin
      write('ERROR');
      exit;
     end;
    writeln('OK');
    write('[*] Create Socket...');
    so:=socket(AF_INET, SOCK_STREAM, 0);
    if so=-1 then begin
    writeln('ERROR');
    exit;
    end;
    writeln('OK');
    
    ca.sin_family:=AF_INET;
    ca.sin_port:=htons(25);
    ca.sin_addr.s_addr:=inet_addr(Pansichar(MX_SERVER));
    write('[*] Connect to server...');
    if connect(so,ca,sizeof(ca))=-1 then begin
    closesocket(so);
    writeln('ERROR');
    exit;
    end;
    writeln('OK');
    recv(so,buf,sizeof(buf),0);
    s:='EHLO server'+#13#10;
    send(so,s[1],length(s),0);
    recv(so,buf,sizeof(buf),0);
    if copy(buf,1,3)<>'250' then
     begin
      writeln('[-] HELO ERROR');
      writeln(buf);
      exit;
     end;
    
    s:='MAIL FROM:<'+MAIL_FROM+'>'+#13#10;
    send(so,s[1],length(s),0);
    recv(so,buf,sizeof(buf),0);
    if copy(buf,1,3)<>'250' then
     begin
      writeln('[-] MAIL FROM ERROR');
      writeln(buf);
      exit;
     end;
    
    s:='RCPT TO:<'+MAIL_TO+'>'+#13#10;
    send(so,s[1],length(s),0);
    recv(so,buf,sizeof(buf),0);
    if copy(buf,1,3)<>'250' then
     begin
      writeln('[-] RCPT TO ERROR');
      writeln(buf);
      exit;
     end;
    
    s:='DATA'+#13#10;
    send(so,s[1],length(s),0);
    recv(so,buf,sizeof(buf),0);
    if copy(buf,1,3)<>'354' then
     begin
      writeln('[-] DATA ERROR');
      writeln(buf);
      exit;
     end;
    
    assignfile(f,mail_file);
    reset(f);
    while not eof(f) do begin
    readln(f,s);
    s:=s+#13#10;
    send(so,s[1],length(s),0);
    end;
    closefile(f);
    s:=#13#10+'.'+#13#10;
    send(so,s[1],length(s),0);
    recv(so,buf,sizeof(buf),0);
    if copy(buf,1,3)<>'250' then
     begin
      writeln('[-] SEND ERROR');
      writeln(buf);
      exit;
     end;
    s:='QUIT'+#13#10;
    send(so,s[1],length(s),0);
    recv(so,buf,sizeof(buf),0);
    if copy(buf,1,3)<>'221' then
     begin
      writeln('[-] QUIT ERROR');
      writeln(buf);
      exit;
     end;
    closehandle(so);
    writeln('[+] SEND OK');
    end.
    
    Для работу нужен модуль WinDNS - его можно где угодно найти в инете.
    или если нужно то вот.
    Code:
    //******************************************************************************
    // Nom           : WinDns.pas
    // Utilisation   : Fonction et Type pour l'acces a DnsApi.dll
    // Auteur        : uncle_khemi@hotmail.com
    // Date          : 27 Aout 2003
    //
    // Modifications :
    // Date          :
    //******************************************************************************
    
    unit WinDNS;
    
    interface
    uses
      Windows;
    
    const
      //  Options for DnsQuery
      DNS_QUERY_STANDARD = 0;
      DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE = 1;
      DNS_QUERY_USE_TCP_ONLY = 2;
      DNS_QUERY_NO_RECURSION = 4;
      DNS_QUERY_BYPASS_CACHE = 8;
    
      //autres
      DNS_ATMA_MAX_ADDR_LENGTH = 20;
      DNS_ATMA_AESA_ADDR_LENGTH = 20;
    
      DNS_TYPE_A = 1;
      DNS_TYPE_CNAME = 5;
      DNS_TYPE_PTR = 12;
      DNS_TYPE_MX = 15;
    
      DNS_UPDATE_SECURITY_USE_DEFAULT = 0;
    
    type
      IP6_ADDRESS = array[0..3] of dword;
      IP4_ADDRESS = DWORD;
      DNS_A_DATA = IP4_ADDRESS;
      DNS_PTR_DATA = PChar;
      DNS_PTR_DATAA = DNS_PTR_DATA;
      DNS_PTR_DATAW = DNS_PTR_DATA;
      DNS_AAAA_DATA = IP6_ADDRESS;
      DNS_STATUS = LongInt;
      PIP4_ARRAY = ^IP4_ARRAY;
    
      //validation d'un nom DNS
      DNS_NAME_FORMAT = (DnsNameDomain,
        DnsNameDomainLabel,
        DnsNameHostnameFull,
        DnsNameHostnameLabel,
        DnsNameWildcard,
        DnsNameSrvRecord);
    
      //definie le type de liberation pour avec DnsFreeRecordList
      DNS_FREE_TYPE = (
        DnsFreeFlat,
        DnsFreeRecordList,
        DnsFreeParsedMessageFields
        );
    
      //tableau d'adresse IP
      IP4_ARRAY = record
        AddrCount: DWORD;
        AddrArray: array[0..10] of IP4_ADDRESS; //[0..10]
      end;
    
      DNS_SRV_DATAA = record
        pNameTarget: PChar;
        wPriority: Word;
        wWeighty: Word;
        wPorty: Word;
        Pady: Word;                         // keep ptrs DWORD aligned
      end;
    
      DNS_TSIG_DATAA = record
        pNameAlgorithm: PChar;
        pAlgorithmPacket: ^Byte;
        pSignature: ^Byte;
        pOtherData: ^Byte;
        i64CreateTime: longlong;
        wFudgeTime: Word;
        wOriginalXid: Word;
        wError: Word;
        wSigLength: Word;
        wOtherLength: Word;
        cAlgNameLength: UCHAR;
        bPacketPointers: Boolean;
      end;
    
      DNS_NXT_DATAA = record
        pNameNext: PChar;
        wNumTypes: Word;
        wTypes: array[0..1] of Word;
      end;
    
      DNS_WINSR_DATA = record
        dwMappingFlag: DWORD;
        dwLookupTimeout: DWORD;
        dwCacheTimeout: DWORD;
        pNameResultDomain: PWideChar;
      end;
    
      DNS_WINSR_DATAA = record
        dwMappingFlag: DWORD;
        dwLookupTimeout: DWORD;
        dwCacheTimeout: DWORD;
        pNameResultDomain: PChar;
      end;
    
      DNS_RECORD_FLAGS = record
        Section: DWORD;                     //DWORD   Section     : 2;
        Delete: DWORD;                      //DWORD   Delete      : 1;
        CharSet: DWORD;                     //DWORD   CharSet     : 2;
        Unused: DWORD;                      //DWORD  Unused      : 3;
        Reserved: DWORD;                    //DWORD  Reserved    : 24;
      end;
    
      DNS_TXT_DATAA = record
        dwStringCount: DWORD;
        pStringArray: array[0..10] of PChar;
      end;
    
      DNS_NULL_DATA = record
        dwByteCount: DWORD;
        Data: array[0..10] of Byte;
      end;
    
      DNS_KEY_DATA = record
        wFlags: Word;
        chProtocol: Byte;
        chAlgorithm: Byte;
        Key: array[0..0] of Byte;
      end;
    
      DNS_SIG_DATAA = record
        pNameSigner: PChar;
        wTypeCovered: Word;
        chAlgorithm: Byte;
        chLabelCount: Byte;
        dwOriginalTtl: DWORD;
        dwExpiration: DWORD;
        dwTimeSigned: DWORD;
        wKeyTag: Word;
        Pad: Word;                          // keep Byte field aligned
        Signature: array[0..0] of Byte;
      end;
    
      DNS_ATMA_DATA = record
        AddressType: Byte;
        Address: array[0..(DNS_ATMA_MAX_ADDR_LENGTH - 1)] of Byte;
      end;
    
      DNS_WKS_DATA = record
        IpAddress: IP4_ADDRESS;
        chProtocol: UCHAR;
        BitMask: array[0..0] of Byte;       // BitMask[1];
      end;
    
      DNS_MX_DATAA = record
        pNameExchange: PChar;
        wPreference: Word;
        Pad: Word;
      end;
    
      DNS_MINFO_DATAA = record
        pNameMailbox: PChar;
        pNameErrorsMailbox: PChar;
      end;
    
      DNS_WINS_DATA = record
        dwMappingFlag: DWORD;
        dwLookupTimeout: DWORD;
        dwCacheTimeout: DWORD;
        cWinsServerCount: DWORD;
        WinsServers: array[0..0] of IP4_ADDRESS;
      end;
    
      DNS_TKEY_DATAA = record
        pNameAlgorithm: PChar;
        pAlgorithmPacket: ^Byte;
        pKey: ^Byte;
        pOtherData: ^Byte;
        dwCreateTime: DWORD;
        dwExpireTime: DWORD;
        wMode: Word;
        wError: Word;
        wKeyLength: Word;
        wOtherLength: Word;
        cAlgNameLength: UCHAR;
        bPacketPointers: Boolean;
      end;
    
      DNS_SOA_DATAA = record
        pNamePrimaryServer: PChar;
        pNameAdministrator: PChar;
        dwSerialNo: DWORD;
        dwRefresh: DWORD;
        dwRetry: DWORD;
        dwExpire: DWORD;
        dwDefaultTtl: DWORD;
      end;
    
      //probleme non resolu lorsqu'on utilise les flags de type S
      TFlags = record
        case Integer of
          1: (DW: DWORD);                   // flags as DWORD
          2: (S: ^DNS_RECORD_FLAGS);        // flags as structure   ???
      end;
    
      TDataA = record
        case Integer of
          1: (A: DNS_A_DATA);               //    A;
          2: (SOA: DNS_SOA_DATAA);          //   SOA, Soa;
          3: (PTR: DNS_PTR_DATAA);          //PTR, Ptr, NS, Ns, CNAME, Cname, MB, Mb, MD, Md, MF, Mf, MG, Mg, MR, Mr;
          4: (MINFO: DNS_MINFO_DATAA);      //MINFO, Minfo,    RP, Rp;
          5: (MX: DNS_MX_DATAA);            //MX, Mx,         AFSDB, Afsdb,             RT, Rt;
          6: (HINFO: DNS_TXT_DATAA);        //HINFO, Hinfo,        ISDN, Isdn,        TXT, Txt,          X25;
          7: (Null: DNS_NULL_DATA);         //Null;
          8: (WKS: DNS_WKS_DATA);           //WKS, Wks;
          9: (AAAA: DNS_AAAA_DATA);         //AAAA;
          10: (KEY: DNS_KEY_DATA);          //KEY, Key;
          11: (SIG: DNS_SIG_DATAA);         //SIG, Sig;
          12: (ATMA: DNS_ATMA_DATA);        //ATMA, Atma;
          13: (NXT: DNS_NXT_DATAA);         //NXT, Nxt;
          14: (SRV: DNS_SRV_DATAA);         //SRV, Srv;
          15: (TKEY: DNS_TKEY_DATAA);       //TKEY, Tkey;
          16: (TSIG: DNS_TSIG_DATAA);       //TSIG, Tsig;
          17: (DWINS: DNS_WINS_DATA);       //WINS, Wins;
          18: (WINSR: DNS_WINSR_DATA);      //WINSR, WinsR, NBSTAT, Nbstat;
      end;
    
    
    
      PDNS_RECORDA = ^DNS_RECORDA;
      DNS_RECORDA = record
        pnext: PDNS_RECORDA;                //  struct _DnsRecordW *
        pName: PChar;                       //PSTR
        wType: Word;                        //WORD                                              //WORD                    wType;
        wDataLength: Word;                  //WORD
        flags: TFlags;                      //
        dwTtl: DWORD;                       //DWORD;
        dwReserved: DWORD;                  //DWORD;
        Data: TDataA;
      end;
    
    
    
      //------------------------------------------------------------------------------
      //Fonctions
      //------------------------------------------------------------------------------
    
    
      //------------------------------------------------------------------------------
      //voir un enregistrement
    function DnsQuery_A(
      pszName: PChar;
      wType: Word;
      Options: DWORD;
      aipServers: PIP4_ARRAY;
      ppQueryResults: Pointer;
      pReserved: Pointer
      ): DNS_STATUS; stdcall; external 'dnsapi.dll';
    //------------------------------------------------------------------------------
    
    //------------------------------------------------------------------------------
    //ajouter, modifier et supprimer un enregistrement
    function DnsModifyRecordsInSet_A(
      pAddRecords: PDNS_RECORDA;
      pDeleteRecords: PDNS_RECORDA;
      Options: DWORD;
      hContext: Hwnd;
      pServerList: PIP4_ARRAY;
      pReserved: Pointer
      ): DNS_STATUS; stdcall; external 'dnsapi.dll';
    //------------------------------------------------------------------------------
    
    //------------------------------------------------------------------------------
    //verifie si un nom DNS est correct
    function DnsValidateName_A(
      pszName: PChar;
      Format: DNS_NAME_FORMAT
      ): DNS_STATUS; stdcall; external 'dnsapi.dll';
    //------------------------------------------------------------------------------
    
    //------------------------------------------------------------------------------
    //supprime la memoire aloue pour la reponse par un DNS_QUERY
    procedure DnsRecordListFree(
      pRecordList: PDNS_RECORDA;
      FreeType: DNS_FREE_TYPE
      ); stdcall; external 'dnsapi.dll';
    //------------------------------------------------------------------------------
    
    //------------------------------------------------------------------------------
    //supprime la memoire aloue pour la reponse par un DNS_QUERY
    //procedure DnsFreeRecordListDeep(
    //  pRecordList: PDNS_RECORDA;
    //  FreeType: DNS_FREE_TYPE
    //  ); stdcall; external 'dnsapi.dll';
    //------------------------------------------------------------------------------
    
    
    
    implementation
    
    end.
    
     
    1 person likes this.
  3. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    но знай - если у почтовика стоит SPF фильтр, то забудь про такой метод отправки.
     
    1 person likes this.
  4. ErrorNeo

    ErrorNeo Elder - Старейшина

    Joined:
    2 May 2009
    Messages:
    923
    Likes Received:
    836
    Reputations:
    402
    +15, slesh

    it works!
     
    #4 ErrorNeo, 3 Aug 2009
    Last edited: 4 Aug 2009
  5. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    Ну яндекс самый лоальный к такого роду письмам.
    А вот на AOL, HOTMAIL и MAIL.RU ты врядли вообще сможешь отправить сообщение.
     
  6. ErrorNeo

    ErrorNeo Elder - Старейшина

    Joined:
    2 May 2009
    Messages:
    923
    Likes Received:
    836
    Reputations:
    402
    *отправил себе примерно 10 сообщений, поленился удалить их из спама - сервер блокнул дальнейший прием сообщений с того IP. Разумно.

    *как добавлять аттачменты(этим методом) - не фкурил, но, почитав фак по командам SMTP пришел к выводу, что скорее всего никак.
    (http://book.itep.ru/4/44/smtp4414.htm - неплохой фак)

    *данные, содержищие специальные и не читаемые символы можно удобно пересылать, предварительно проеобразовав их в hex формат.
     
  7. ErrorNeo

    ErrorNeo Elder - Старейшина

    Joined:
    2 May 2009
    Messages:
    923
    Likes Received:
    836
    Reputations:
    402
    4 функции для преобразования текста в hex и обратно:
    построчно и по-символьно.
    Если вдруг вам когда-либо нужно будет надо переслать себе по почте(из консоли) файл, содержащий служебные или спец. символы...
    Code:
    {преобразовать строку в 16-ричный формат}
    function StringToHex(S: String): String;
    var I: Integer;
    begin
      Result:= '';
      for I := 1 to length (S) do
        Result:= Result+IntToHex(ord(S[i]),2);
    end;
    
    {преобразовать символ в 16-ричный формат}
    function ChrToHex(S: Char): String;
    begin
      Result:= IntToHex(ord(S),2);
    end;
    
    {преобразовать строку в 16-ричном формате обратно в текст}
    function HexToString(H: String): String;
    var I: Integer;
    begin
      Result:= '';
      for I := 1 to length (H) div 2 do
        Result:= Result+Char(StrToInt('$'+Copy(H,(I-1)*2+1,2)));
    end;
    
    {преобразовать "символ" в 16-ричном формате в обычный символ}
    function HexToChr(H: String): Char;
    begin
        Result:= Char(StrToInt('$'+Copy(H,1,2)));
    end;
    очень удобно. И не надо никаких аттачментов.
     
    #7 ErrorNeo, 4 Aug 2009
    Last edited: 4 Aug 2009
  8. aqqa

    aqqa Banned

    Joined:
    12 Jul 2008
    Messages:
    96
    Likes Received:
    16
    Reputations:
    3
    Чувак взгляни на это.....
    http://www.glob.com.au/sendmail/sendmail.zip
    Работает 100% !!!!
     
  9. ErrorNeo

    ErrorNeo Elder - Старейшина

    Joined:
    2 May 2009
    Messages:
    923
    Likes Received:
    836
    Reputations:
    402
    хорошая ссылка, пусть тоже будет тут.

    зы. 100% того, мне было "нужно" я уже реализовал с помощью кода slesh. Причем, как и хотел, с использованием одного лишь winsock.

    Сюда же докидываю инфу, которая может в дальнейшем пригодиться тем, кому так же как и мне нужно будет наладить пересылку сообщений (и файлов) по SMTP из консоли средствами одного лишь win-api
     
  10. ErrorNeo

    ErrorNeo Elder - Старейшина

    Joined:
    2 May 2009
    Messages:
    923
    Likes Received:
    836
    Reputations:
    402
    обратил внимание, что на этот код просто _нереально_ "обозлилась" целая куча а-вирей. =\
    Не кошерно.
    Использовать 'as is' нельзя.
    Проблема, как ни странно, решается редактированием всего-лишь одной строки кода.
     
    #10 ErrorNeo, 5 Aug 2009
    Last edited: 5 Aug 2009