1.) Texture generation is replaced by vertex shaders, so if you have one, you need to write your own texture coord generation code inside it.
2.) Samplers are integers. You must use glUniform1i() to load the texture image unit id into your sampler (See OpenGL 2.1 spec page 82.)
3.) glUniform*() calls have no program parameter, they work on the currently active program, means they must be called after glUseProgram(yourProgram).
4.) Shaders use texture image units. There is no need to enable or disable the fixed pipeline texture units. That’s implicitly done by the shaders.
5.) What’s your hardware and drivers?
6.) This renders something without the shader?
7.) With the shader, is your object rendered at all or do you see only a black screen? Try glClearColor(0.5f, 0.0f, 0.5f, 0.0f) dark magenta to see if you rendered black.
8.) Show your complete shader sources.
9.) What are the face.GetImageWidth() and face.GetImageHeight() for all six textures? (square power-of-two?)
10.) Do you see something if you just download single colored 1x1 textures? Use a different color per face, no black.
11.) What’s your glPixelStore(GL_UNPACK_ALIGNMENT)? Use 1! Mind the default is 4 and GL_RGB will only line up with multiple-of-4 sizes correctly, should show skewed images otherwise.
Zero is a valid texture “object” which is reserved for the immediate textures.
glBindTexture(_, 0) is the only way to switch back to that immediate texture once you really used a texture object id != 0.
It’s probably not what he wanted, but otherwise should just work.