/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "Licence").
 * You may not use this file except in compliance with the Licence.
 *
 * You can obtain a copy of the licence at RISC OS path @.^.LICENCE
 * or  http://www.riscosdev.com/lanman98/LICENCE.CDDL
 * See the Licence for the specific language governing permissions
 * and limitations under the Licence.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the Licence file. If applicable, add the
 * following below this CDDL HEADER, with the fields enclosed by
 * brackets "[]" replaced with your own identifying information:
 * Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
 
/*
 *   Copyright 1996 Warm Silence Software Ltd.  All rights reserved.
 *   Use is subject to license terms.
 */

/*   PHBG 9/8/97: Initial version
 */

#include <stdio.h>
#include "LanMan98BaseLib/error.h"

#include "smberr.h"

#define ERRDOS (1)
#define ERRSRV (2)
#define ERRHRD (3)

/*
    NT status codes, and the DOS error class and code each stands in for.

    A server that negotiates CAP_STATUS32 answers with these instead of the
    class and code pair, and they say considerably more: an access denied,
    a logon failure and a sharing violation all arrive as ERRDOS/ERRnoaccess
    in the old scheme and are indistinguishable once they get here.

    Mapping each one back to its DOS equivalent keeps the error numbers the
    rest of the filing system tests against unchanged - 0x10002 for a missing
    file, 0x10003 for a missing directory, and so on - while the message the
    user sees names the status the server actually sent.
*/

typedef struct
{
    unsigned int status;
    unsigned short err;
    unsigned short suberr;
    char *text;
} nt_status_t;

static nt_status_t nt_statuses[] =
{
    { 0x00000000u, 0,      0,     "No error" },
    { 0x80000005u, ERRDOS, 234,   "More data is available" },
    { 0x80000006u, ERRDOS, 18,    "A File Search command can find no more files" },
    { 0xC0000002u, ERRDOS, 1,     "Not implemented by the server" },
    { 0xC0000008u, ERRDOS, 6,     "Invalid file handle" },
    { 0xC000000Du, ERRDOS, 87,    "Invalid parameter" },
    { 0xC000000Eu, ERRDOS, 15,    "Invalid drive specified" },
    { 0xC000000Fu, ERRDOS, 2,     "File not found" },
    { 0xC0000010u, ERRDOS, 1,     "Invalid function" },
    { 0xC0000011u, ERRDOS, 38,    "Unexpected end of file" },
    { 0xC0000016u, ERRSRV, 1,     "More processing required" },
    { 0xC0000022u, ERRDOS, 5,     "Access denied" },
    { 0xC0000023u, ERRDOS, 111,   "Buffer too small" },
    { 0xC0000033u, ERRSRV, 67,    "Invalid file spec" },
    { 0xC0000034u, ERRDOS, 2,     "File not found" },
    { 0xC0000035u, ERRDOS, 80,    "File already exists" },
    { 0xC0000039u, ERRDOS, 3,     "Directory invalid" },
    { 0xC000003Au, ERRDOS, 3,     "Directory invalid" },
    { 0xC000003Bu, ERRDOS, 3,     "Malformed path name" },
    { 0xC0000043u, ERRDOS, 32,    "Sharing mode conflict" },
    { 0xC0000054u, ERRDOS, 33,    "Lock conflict" },
    { 0xC0000055u, ERRDOS, 33,    "Lock conflict" },
    { 0xC0000056u, ERRDOS, 5,     "The file is being deleted" },
    { 0xC0000064u, ERRSRV, 2,     "No such user" },
    { 0xC0000068u, ERRSRV, 2,     "This user may not log on from here" },
    { 0xC000006Au, ERRSRV, 2,     "Bad password" },
    { 0xC000006Du, ERRSRV, 2,     "Logon failure: check the user name and password" },
    { 0xC000006Eu, ERRSRV, 2,     "This account is restricted" },
    { 0xC000006Fu, ERRSRV, 2,     "This account may not log on at this time" },
    { 0xC0000070u, ERRSRV, 2,     "This account may not log on from this machine" },
    { 0xC0000071u, ERRSRV, 2242,  "The password of this user has expired" },
    { 0xC0000072u, ERRSRV, 2,     "This account is disabled" },
    { 0xC000007Fu, ERRDOS, 112,   "There is not enough space on the disk" },
    { 0xC000009Au, ERRSRV, 89,    "No resources currently available for request" },
    { 0xC00000A2u, ERRHRD, 19,    "Write-protected media" },
    { 0xC00000A3u, ERRHRD, 21,    "Drive not ready" },
    { 0xC00000BAu, ERRDOS, 5,     "That is a directory, not a file" },
    { 0xC00000BBu, ERRSRV, 65535, "Function not supported" },
    { 0xC00000BEu, ERRSRV, 6,     "Bad network path" },
    { 0xC00000C9u, ERRSRV, 5,     "The network name was deleted" },
    { 0xC00000CAu, ERRDOS, 5,     "Access to the network resource was denied" },
    { 0xC00000CCu, ERRSRV, 6,     "Share name does not exist" },
    { 0xC00000D0u, ERRSRV, 89,    "The server refused the request" },
    { 0xC0000101u, ERRDOS, 5,     "The directory is not empty" },
    { 0xC0000103u, ERRDOS, 3,     "That is not a directory" },
    { 0xC000011Fu, ERRDOS, 4,     "Too many open files" },
    { 0xC0000121u, ERRDOS, 5,     "Cannot delete" },
    { 0xC000015Bu, ERRSRV, 4,     "This account may not log on to this service" },
    { 0xC0000193u, ERRSRV, 2239,  "This user account has expired" },
    { 0xC0000203u, ERRSRV, 91,    "The Uid is not known as a valid user" },
    { 0xC0000205u, ERRSRV, 89,    "Insufficient server resources" },
    { 0xC0000224u, ERRSRV, 2242,  "The password of this user must be changed" },
    { 0xC0000234u, ERRSRV, 2,     "This account is locked out" },
    { 0u, 0, 0, NULL }
};

static nt_status_t *lookup_status(unsigned int status)
{
    int i;

    for(i = 0; nt_statuses[i].text; i++)
        if(nt_statuses[i].status == status)
            return &nt_statuses[i];
    return NULL;
}

int SMBStatusToDos(unsigned int status, int *err, int *suberr)
{
    nt_status_t *rec;

    rec = lookup_status(status);
    if(rec)
    {
        *err = rec->err;
        *suberr = rec->suberr;
        return 1;
    }
    if((status & 0xC0000000u) == 0)
    {
        /* Severity success: informational, and not an error however
           unfamiliar the particular code is. */
        *err = 0;
        *suberr = 0;
        return 1;
    }
    /* Unknown, and a warning or an error.  ERRSRV with a code of zero is
       used for these because nothing tests for it, so an unrecognised
       status cannot be mistaken for one that is handled - in particular
       not for ERRSRV/ERRerror, which drives the transaction retry loop. */
    *err = ERRSRV;
    *suberr = 0;
    return 0;
}

void SMBErrorStatus(unsigned int status)
{
    static char buf[256];
    nt_status_t *rec;
    int err, suberr;

    SMBStatusToDos(status, &err, &suberr);
    rec = lookup_status(status);
    if(rec)
        sprintf(buf, "%s (NT status 0x%08X)", rec->text, status);
    else
        sprintf(buf, "SMB error, NT status 0x%08X", status);
    ErrorNum(((err << 16) | suberr), buf);
}

void SMBError(int err, int suberr)
{
    static char buf[256];
    int num;

    num = ((err << 16) | suberr);
    // printf("Error 0x%04x\n", num);
    switch(err)
    {
        case 1:
            switch(suberr)
            {
                case 1: ErrorNum(num, "Invalid function");
                case 2: ErrorNum(num, "File not found");
                case 3: ErrorNum(num, "Directory invalid");
                case 4: ErrorNum(num, "Too many open files");
                case 5: ErrorNum(num, "Access denied");
                case 6: ErrorNum(num, "Invalid file handle");
                case 7: ErrorNum(num, "Memory control blocks destroyed");
                case 8: ErrorNum(num, "Insufficient server memory to perform the requested function");
                case 9: ErrorNum(num, "Invalid memory block address");
                case 10: ErrorNum(num, "Invalid environment");
                case 11: ErrorNum(num, "Invalid format");
                case 12: ErrorNum(num, "Invalid open mode");
                case 13: ErrorNum(num, "Invalid data");
                case 15: ErrorNum(num, "Invalid drive specified");
                case 16: ErrorNum(num, "Attempt to delete current directory");
                case 17: ErrorNum(num, "Not same device");
                case 18: ErrorNum(num, "A File Search command can find no more files");
                case 32: ErrorNum(num, "Sharing mode conflict");
                case 33: ErrorNum(num, "Lock conflict");
                case 67: ErrorNum(num, "Share name does not exist");
                case 80: ErrorNum(num, "File already exists");
                case 112: ErrorNum(num, "There is not enough space on the disk");
            }
            break;
        case 2:
            switch(suberr)
            {
                case 1: ErrorNum(num, "Non-specific error");
                case 2: ErrorNum(num, "Bad password");
                case 4: ErrorNum(num, "Insufficient access rights");
                case 5: ErrorNum(num, "Invalid TID.");
                case 6: ErrorNum(num, "Invalid network name in tree connect.");
                case 7: ErrorNum(num, "Invalid device");
                case 49: ErrorNum(num, "Print queue full");
                case 50: ErrorNum(num, "Print queue full");
                case 51: ErrorNum(num, "EOF on print queue dump.");
                case 52: ErrorNum(num, "Invalid print file FID.");
                case 64: ErrorNum(num, "Unrecognized command");
                case 65: ErrorNum(num, "Internal server error");
                case 67: ErrorNum(num, "Invalid file spec");
                case 69: ErrorNum(num, "Invalid setting of access permissions");
                case 71: ErrorNum(num, "Invalid attribute mode");
                case 81: ErrorNum(num, "Server is paused");
                case 82: ErrorNum(num, "Not receiving messages");
                case 83: ErrorNum(num, "No room to buffer message");
                case 87: ErrorNum(num, "Too many remote user names");
                case 88: ErrorNum(num, "Operation timed out");
                case 89: ErrorNum(num, "No resources currently available for request");
                case 90: ErrorNum(num, "Too many Uids active on this session");
                case 91: ErrorNum(num, "The Uid is not known as a valid user");
                case 250: ErrorNum(num, "Temporarily unable to support Raw mode");
                case 251: ErrorNum(num, "Temporarily unable to support Raw mode");
                case 252: ErrorNum(num, "Continue in MPX mode");
                case 2239: ErrorNum(num, "This user account has expired");
                case 2242: ErrorNum(num, "The password of this user has expired");
                case 65535: ErrorNum(num, "Function not supported");
            }
            break;
        case 3:
            switch(suberr)
            {
                case 19: ErrorNum(num, "Write-protected media");
                case 20: ErrorNum(num, "Unknown unit");
                case 21: ErrorNum(num, "Drive not ready");
                case 22: ErrorNum(num, "Unknown command");
                case 23: ErrorNum(num, "Data error (CRC)");
                case 24: ErrorNum(num, "Bad request structure length");
                case 25: ErrorNum(num, "Seek error");
                case 26: ErrorNum(num, "Unknown media type");
                case 27: ErrorNum(num, "Sector not found");
                case 28: ErrorNum(num, "Printer out of paper");
                case 29: ErrorNum(num, "Write fault");
                case 30: ErrorNum(num, "Read fault");
                case 31: ErrorNum(num, "General failure");
                case 32: ErrorNum(num, "An open conflicts with an existing open");
                case 33: ErrorNum(num, "Lock request conflict");
                case 34: ErrorNum(num, "The wrong disk was found in a drive");
                case 35: ErrorNum(num, "No FCBs are available to process request");
                case 36: ErrorNum(num, "A sharing buffer has been exceeded");
            }
            break;
    }
    sprintf(buf, "SMB error 0x%02X, 0x%04X", err, suberr);
    ErrorNum(num, buf);
}
