1 // Written in the D programming language.
2 
3 /* Example application for SAP NetWeaver RFC
4    Reads a saplogon.ini file and creates a sapnwrfc.ini from the data.
5 */
6 module createini;
7 
8 private import std.string;
9 private import std.stdio;
10 private import std.traits : isSomeString;
11 
12 import etc.c.sapnwrfc : RFC_CONNECTION_PARAMETER, cU;
13 import etc.c.sapuc : strlenU16;
14 
15 enum VERSION = "0.1";
16     
17 /*
18  * Function to read the saplogon.ini file.
19  *
20  * For a description of the file format, please see https://service.sap.com/sap/support/notes/99435.
21  */
22 
23 // Parses the lines of the ini file
24 string[string][string] parse(string[] lines)
25 {
26     string[string][string] res;
27     auto section = "";
28 
29     foreach(line; lines)
30     {
31         line = line.strip;
32 
33         // Ignore empty or comment lines
34         if (line.length == 0 || line.startsWith(";"))
35             continue;
36         
37         // Start of new section?
38         if (line.startsWith("[") && line.endsWith("]"))
39         {
40             section = line[1..line.length-1];
41         }
42         else
43         {
44             auto idx = line.indexOf('=');
45             if (idx < 1)
46                 continue; // Ignore bad lines
47             
48             auto key = line[0..idx].strip;
49             auto value = line[idx+1..line.length].strip;
50             res[section][key] = value;
51         }
52     }
53 
54     return res;
55 }
56 
57 // Maps the parsed values of the ini file to RFC_CONNECTION_PARAMETER values
58 RFC_CONNECTION_PARAMETER[][string] map(string[string][string] raw)
59 {
60     RFC_CONNECTION_PARAMETER[][string] res;
61 
62     foreach (item, sysname; raw["MSSysName"])
63     {
64         // Ignore systems without a "MSSysName"
65         if (sysname == "")
66             continue;
67 
68         RFC_CONNECTION_PARAMETER[10] params;
69         int nextParam = 0;
70 
71         void set(wstring param, string key)
72         {
73             auto val = raw[key][item];
74             if (val.length)
75             {
76                 params[nextParam++] = RFC_CONNECTION_PARAMETER(param.ptr, cU(val));
77             }
78         }
79 
80         params[nextParam++] = RFC_CONNECTION_PARAMETER("DEST"w.ptr, cU(sysname));
81         auto origin = raw["Origin"][item];
82         auto server = raw["Server"][item];
83         if (origin == "MS_SEL_GROUPS")
84         {
85             set("GROUP", "Server");
86             //set("MSHOST", "MSSrvName");
87             // Assuming that "MSSysName" is always present
88             // set(Parameter.SYSID, "MSSysName");
89         }
90         else //origin == "MS_SEL_SERVER" || origin == "USEREDIT"
91         {
92             set("ASHOST", "Server");
93             // Assuming that "Database" is always present
94             // set(Parameter.SYSNR, "Database");
95         }
96         set("SYSNR", "Database");
97         set("MSHOST", "MSSrvName");
98         set("MSSERV", "MSSrvPort");
99         set("SAPROUTER", "Router");
100         auto cpi = raw["CodepageIndex"][item];
101         if (cpi.length > 0 && cpi != "-1")
102             set("CODEPAGE", "Codepage");
103         auto sncChoice = raw["SncChoice"][item];
104         if (sncChoice.length > 0 && sncChoice != "-1")
105         {
106             set("SNC_QOP", "SncChoice");
107             set("SNC_PARTNERNAME", "SncName");
108             set("SNC_SSO", raw["SncNoSSO"][item] == "0" ? "1" : "0");
109         }
110 
111         res[sysname] = params[0..nextParam].dup;
112     }
113     return res;
114 }
115 
116 unittest
117 {
118     string[] lines = [
119         "[Origin]",
120         "Item1=USEREDIT",
121         "; This is a comment!"
122     ];
123     auto res = parse(lines);
124     assert("Origin" in res);
125     assert("Item1" in res["Origin"]);
126     assert(res["Origin"]["Item1"] == "USEREDIT");
127     writeln("Success.");
128 }
129 
130 void usage()
131 {
132     writefln("CreateIni V%s", VERSION);
133     writeln("\nUsage:");
134     writeln("    createinit <SAPLOGONINI>");
135     writeln("\nParameter:");
136     writeln("    <SAPLOGONINI>    Path to saplogon.ini");
137     writeln("\nExample:");
138     writeln("    createini C:\\Windows\\saplogon.ini");
139 }
140 
141 int main(string[] args)
142 {
143     if (args.length != 2)
144     {
145         usage();
146         return 1;       
147     }
148     
149     import std.file : read;
150     auto rawcontent = read(args[1]);
151     version (Windows)
152     {
153         import core.sys.windows.windows;
154         import std.windows.syserror;
155         static import std.utf;
156         
157         wchar[] result;
158         int readLen;
159         result.length = MultiByteToWideChar(CP_ACP, 0, cast(const(char)*)rawcontent.ptr, cast(int)rawcontent.length, null, 0);
160         if (result.length)
161         {
162             readLen = MultiByteToWideChar(CP_ACP, 0, cast(const(char)*)rawcontent.ptr, cast(int)rawcontent.length, result.ptr, cast(int)result.length);
163         }
164         if (!readLen || readLen != result.length)
165         {
166             throw new Exception("Couldn't convert string: " ~ sysErrorString(GetLastError()));
167         }
168         auto content = std.utf.toUTF8(result).split("\n");
169     }
170     else
171         auto content = rawcontent.split("\n");
172 
173     auto config = map(parse(content));
174     foreach (name, params; config)
175     {
176         foreach (param; params)
177         {
178             writefln("%s=%s", param.name[0..strlenU16(param.name)], param.value[0..strlenU16(param.value)]);
179         }
180         writeln();
181     }
182     
183     return 0;
184 }